]> git.the-white-hart.net Git - vhdl/commitdiff
Add STM interface and host test program
authorrs <>
Tue, 11 Nov 2025 21:40:00 +0000 (15:40 -0600)
committerrs <>
Tue, 11 Nov 2025 21:40:00 +0000 (15:40 -0600)
.gitignore
libraries/nexys2/stmex_wb.vhd [new file with mode: 0644]
libraries/nexys2/tests/nexys2_stm.vhd [new file with mode: 0644]
libraries/nexys2/tests/test_nexys2stm.vhd [new file with mode: 0644]
libraries/nexys2/tests/test_stmex.vhd [new file with mode: 0644]
projects/nexys2_host_controller/host/build.sh
projects/nexys2_host_controller/host/stm.c [new file with mode: 0644]

index b0d8c719f7555e02a70772a7c0256f7b64c1d284..889539a656534322d0042502999d7a738cab7fd4 100644 (file)
@@ -18,5 +18,6 @@ projects/nexys2_host_controller/host/epp_read
 projects/nexys2_host_controller/host/epp_write
 projects/nexys2_host_controller/host/digdude
 projects/nexys2_host_controller/host/jtag
+projects/nexys2_host_controller/host/stm
 
 .idea/
diff --git a/libraries/nexys2/stmex_wb.vhd b/libraries/nexys2/stmex_wb.vhd
new file mode 100644 (file)
index 0000000..bb066af
--- /dev/null
@@ -0,0 +1,164 @@
+--------------------------------------------------------------------------------
+-- dl_stb_o - a byte from the host is ready for the device
+-- dl_rdy_i - the device is ready to accept bytes (valid even when dl_stb_o unasserted - not an ack!)
+-- dl_dat_o - current byte to stream download from the host
+-- dl_end_o - byte transfered in previous clock cycle was the final byte
+--------------------------------------------------------------------------------
+-- ul_stb_i - a byte from the device is ready for the host
+-- ul_rdy_o - the host acknowledges the byte from the device
+-- ul_dat_i - byte to stream upload to the host
+-- ul_end_i - extra bit associated with ul_dat_i indicating that this is the last byte in a transfer
+--------------------------------------------------------------------------------
+
+-- There is a potential problem here that doesn't look like it can be solved.
+-- When ending a transfer, the controller will deassert FLAGA/B to signal that
+-- no more bytes are available for download or no more space is free for upload.
+-- The maximum potential delay between the IFCLK edge and the FLAGx changing is
+-- listed in the datasheet as T_XFLG = 9.6 ns.  Since the IFCLK period is 20.83
+-- ns, that leaves 11.33 ns of slack before the next edge.  However, the setup
+-- time for the SLRD and SLWR signals, T_SRD/T_SWR is 18.7 ns.  So when the
+-- controller signals with the FLAGx signals that a transfer is complete, there
+-- isn't enough time left within that cycle to deassert SLRD/SLWR while still
+-- meeting timing.  Leaving SLRD/SLWR asserted and releasing them in the next
+-- cycle feels like it would be a workable solution (since there's nothing left
+-- to transfer anyway), but some of the FIFO logic appears to still be active
+-- and future transfers get messed up when this is done.  So here, we just
+-- release the SLRD/SLWR signals within the same clock cycle, potentially too
+-- late, and it seems to accidentally work well enough in practice.
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity stmex_wb is
+       port (
+               -- STM-bus signals
+               ifclk:    in    std_logic;
+               stmen:    in    std_logic;
+               db_i:     in    std_logic_vector(7 downto 0);
+               db_o:     out   std_logic_vector(7 downto 0);
+               flaga:    in    std_logic;
+               flagb:    in    std_logic;
+               fifoadr:  out   std_logic_vector(1 downto 0);
+               slrd:     out   std_logic;
+               slwr:     out   std_logic;
+               sloe:     out   std_logic;
+               pktend:   out   std_logic;
+
+               -- Download (host to device) Wishbone-like bus signals
+               dl_stb_o: out   std_logic;
+               dl_rdy_i: in    std_logic;
+               dl_dat_o: out   std_logic_vector(7 downto 0);
+               dl_end_o: out   std_logic;
+
+               -- Upload (device to host) Wishbone-like bus signals
+               ul_stb_i: in    std_logic;
+               ul_ack_o: out   std_logic;
+               ul_dat_i: in    std_logic_vector(7 downto 0);
+               ul_end_i: in    std_logic
+       );
+end stmex_wb;
+
+
+architecture behavioral of stmex_wb is
+
+       type state_t is (S_IDLE, S_DOWNLOAD, S_UPLOAD, S_PKTEND);
+       signal state_cur:  state_t;
+       signal state_next: state_t;
+
+       signal fifoadr_int: std_logic_vector(1 downto 0);
+       signal slrd_int:    std_logic;
+       signal slwr_int:    std_logic;
+       signal sloe_int:    std_logic;
+       signal pktend_int:  std_logic;
+
+begin
+
+       dl_dat_o <= db_i;
+
+       -- Signals are externally pulled up, gate on stmen
+       fifoadr <= fifoadr_int;
+       slrd    <= slrd_int;
+       slwr    <= slwr_int;
+       sloe    <= sloe_int;
+       pktend  <= pktend_int;
+       db_o    <= ul_dat_i;
+
+       process (ifclk, stmen, state_next)
+       begin
+               if rising_edge(ifclk) then
+                       if stmen = '0' then
+                               state_cur <= S_IDLE;
+                       else
+                               state_cur <= state_next;
+                       end if;
+               end if;
+       end process;
+
+       process (state_cur, flaga, flagb, dl_rdy_i, ul_stb_i, ul_end_i)
+       begin
+               state_next  <= state_cur;
+
+               slrd_int    <= '1';
+               slwr_int    <= '1';
+               sloe_int    <= '1';
+               pktend_int  <= '1';
+               fifoadr_int <= "00";
+
+               dl_stb_o    <= '0';
+               dl_end_o    <= '0';
+               ul_ack_o    <= '0';
+
+               case state_cur is
+                       when S_IDLE =>
+                               -- Select fifoadr before entering upload/download state because the setup time is longer than one ifclk cycle
+                               -- The max delay for the flagx signals should leave plenty of clock left to meet setup time for fifoadr
+                               -- If setup time for FIFOADR still can't be met for some reason, entering an S_PRE_DOWNLOAD or S_PRE_UPLOAD
+                               -- state before the S_DOWNLOAD or S_UPLOAD states would give a full cycle to assert the correct FIFOADR.
+                               if flaga = '0' and dl_rdy_i = '1' then
+                                       fifoadr_int <= "00";
+                                       state_next <= S_DOWNLOAD;
+                               elsif flagb = '0' and ul_stb_i = '1' then
+                                       fifoadr_int <= "10";
+                                       state_next <= S_UPLOAD;
+                               end if;
+
+                       when S_DOWNLOAD =>
+                               fifoadr_int <= "00";
+                               sloe_int    <= '0';
+                               slrd_int    <= '0';
+                               dl_stb_o    <= '1';
+
+                               if flaga = '1' or dl_rdy_i = '0' then
+                                       slrd_int   <= '1';
+                                       dl_stb_o   <= '0';
+                                       dl_end_o   <= '1';
+                                       state_next <= S_IDLE;
+                               end if;
+
+                       when S_UPLOAD =>
+                               fifoadr_int <= "10";
+                               slwr_int    <= '0';
+                               ul_ack_o    <= '1';
+
+                               if flagb = '1' or ul_stb_i = '0' then
+                                       slwr_int   <= '1';
+                                       ul_ack_o   <= '0';
+                                       state_next <= S_IDLE;
+                               elsif ul_end_i = '1' then
+                                       -- If the device signals an end-of-packet for this byte, leave SLWR asserted since
+                                       -- this byte still needs to be accepted.
+                                       state_next <= S_PKTEND;
+                               end if;
+
+                       when S_PKTEND =>
+                               fifoadr_int <= "10";
+                               pktend_int  <= '0';
+                               state_next  <= S_IDLE;
+
+                       when others =>
+                               state_next <= S_IDLE;
+               end case;
+       end process;
+
+end behavioral;
diff --git a/libraries/nexys2/tests/nexys2_stm.vhd b/libraries/nexys2/tests/nexys2_stm.vhd
new file mode 100644 (file)
index 0000000..5944de4
--- /dev/null
@@ -0,0 +1,97 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+library utility;
+
+library work;
+
+
+entity nexys2_stm is
+       port (
+               clk_50:     in    std_logic;
+
+               DB:         inout std_logic_vector(7 downto 0);
+               DstmIFCLK:  in    std_logic;
+               DstmSLCS:   in    std_logic;
+               DstmFLAGA:  in    std_logic;
+               DstmFLAGB:  in    std_logic;
+               DstmADR:    out   std_logic_vector(1 downto 0);
+               DstmSLRD:   out   std_logic;
+               DstmSLWR:   out   std_logic;
+               DstmSLOE:   out   std_logic;
+               DstmPKTEND: out   std_logic
+       );
+end nexys2_stm;
+
+
+architecture behavioral of nexys2_stm is
+
+       signal dl_stb:  std_logic;
+       signal dl_rdy:  std_logic;
+       signal dl_dat:  std_logic_vector(7 downto 0);
+
+       signal ul_stb:  std_logic;
+       signal ul_ack:  std_logic;
+       signal ul_dat:  std_logic_vector(7 downto 0);
+
+       signal int_stb: std_logic;
+       signal int_ack: std_logic;
+       signal int_dat: std_logic_vector(7 downto 0);
+
+begin
+
+       e_stm_wb: entity work.stmex_wb
+               port map (
+                       ifclk    => DstmIFCLK,
+                       stmen    => DstmSLCS,
+                       db       => DB,
+                       flaga    => DstmFLAGA,
+                       flagb    => DstmFLAGB,
+                       fifoadr  => DstmADR,
+                       slrd     => DstmSLRD,
+                       slwr     => DstmSLWR,
+                       sloe     => DstmSLOE,
+                       pktend   => DstmPKTEND,
+
+                       dl_stb_o => dl_stb,
+                       dl_rdy_i => dl_rdy,
+                       dl_dat_o => dl_dat,
+                       dl_end_o => open,
+
+                       ul_stb_i => ul_stb,
+                       ul_rdy_o => ul_ack,
+                       ul_dat_i => ul_dat,
+                       ul_end_i => '0'
+               );
+
+       e_dl_fifo: entity utility.fifo_xclk
+               port map (
+                       head_clk_i => DstmIFCLK,
+                       head_stb_i => dl_stb,
+                       head_ack_o => dl_rdy,
+                       head_dat_i => dl_dat,
+
+                       tail_clk_i => clk_50,
+                       tail_stb_o => int_stb,
+                       tail_ack_i => int_ack,
+                       tail_dat_o => int_dat
+               );
+
+       e_ul_fifo: entity utility.fifo_xclk
+               port map (
+                       head_clk_i => clk_50,
+                       head_stb_i => int_stb,
+                       head_ack_o => int_ack,
+                       head_dat_i => int_dat,
+
+                       tail_clk_i => DstmIFCLK,
+                       tail_stb_o => ul_stb,
+                       tail_ack_i => ul_ack,
+                       tail_dat_o => ul_dat
+               );
+
+end behavioral;
diff --git a/libraries/nexys2/tests/test_nexys2stm.vhd b/libraries/nexys2/tests/test_nexys2stm.vhd
new file mode 100644 (file)
index 0000000..d38660c
--- /dev/null
@@ -0,0 +1,120 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library simulated;
+
+
+entity test_nexys2stm is
+end test_nexys2stm;
+
+
+architecture behavior of test_nexys2stm is
+
+       signal db:       std_logic_vector(7 downto 0);
+       signal ifclk:    std_logic;
+       signal slcs:     std_logic;
+       signal flaga:    std_logic;
+       signal flagb:    std_logic;
+       signal fifoadr:  std_logic_vector(1 downto 0);
+       signal slrd:     std_logic;
+       signal slwr:     std_logic;
+       signal sloe:     std_logic;
+       signal pktend:   std_logic;
+
+       signal dl_data:  std_logic_vector(7 downto 0);
+       signal ul_count: integer;
+
+       signal clk_50:   std_logic;
+
+begin
+
+       p_test: process
+       begin
+               DB <= (others => 'Z');
+               wait for 50 ns;
+               dl_data <= x"a0"; wait for 0 ns;
+               dl_data <= x"a1"; wait for 0 ns;
+               dl_data <= x"a2"; wait for 0 ns;
+               dl_data <= x"a3"; wait for 0 ns;
+               dl_data <= x"a4"; wait for 0 ns;
+               dl_data <= x"a5"; wait for 0 ns;
+               dl_data <= x"a6"; wait for 0 ns;
+               dl_data <= x"a7"; wait for 0 ns;
+               ul_count <= 8; wait for 0 ns;
+
+               wait for 600 ns;
+
+               dl_data <= x"b0"; wait for 0 ns;
+               dl_data <= x"b1"; wait for 0 ns;
+               dl_data <= x"b2"; wait for 0 ns;
+               dl_data <= x"b3"; wait for 0 ns;
+               dl_data <= x"b4"; wait for 0 ns;
+               dl_data <= x"b5"; wait for 0 ns;
+               dl_data <= x"b6"; wait for 0 ns;
+               dl_data <= x"b7"; wait for 0 ns;
+               ul_count <= 8; wait for 0 ns;
+
+               --dl_data <= x"c0"; wait for 0 ns;
+               --dl_data <= x"c1"; wait for 0 ns;
+               --dl_data <= x"c2"; wait for 0 ns;
+               --dl_data <= x"c3"; wait for 0 ns;
+               --dl_data <= x"c4"; wait for 0 ns;
+               --dl_data <= x"c5"; wait for 0 ns;
+               --dl_data <= x"c6"; wait for 0 ns;
+               --dl_data <= x"c7"; wait for 0 ns;
+               --dl_data <= x"d0"; wait for 0 ns;
+               --dl_data <= x"d1"; wait for 0 ns;
+               --dl_data <= x"d2"; wait for 0 ns;
+               --dl_data <= x"d3"; wait for 0 ns;
+               --dl_data <= x"d4"; wait for 0 ns;
+               --dl_data <= x"d5"; wait for 0 ns;
+               --dl_data <= x"d6"; wait for 0 ns;
+               --dl_data <= x"d7"; wait for 0 ns;
+
+               wait;
+       end process;
+
+
+       e_host: entity simulated.stmhost
+               port map (
+                       ifclk         => ifclk,
+                       slcs          => slcs,
+                       flaga         => flaga,
+                       flagb         => flagb,
+                       slrd          => slrd,
+                       slwr          => slwr,
+                       sloe          => sloe,
+                       pktend        => pktend,
+                       fifoadr       => fifoadr,
+                       db            => db,
+
+                       host_dl_data  => dl_data,
+                       host_ul_count => ul_count
+               );
+
+
+       e_uut: entity work.nexys2_stm
+               port map (
+                       clk_50     => clk_50,
+                       DB         => db,
+                       DstmIFCLK  => ifclk,
+                       DstmSLCS   => slcs,
+                       DstmFLAGA  => flaga,
+                       DstmFLAGB  => flagb,
+                       DstmADR    => fifoadr,
+                       DstmSLRD   => slrd,
+                       DstmSLWR   => slwr,
+                       DstmSLOE   => sloe,
+                       DstmPKTEND => pktend
+               );
+
+       p_clk: process
+       begin
+               clk_50 <= '0';
+               wait for 10 ns;
+               clk_50 <= '1';
+               wait for 10 ns;
+       end process;
+
+end;
diff --git a/libraries/nexys2/tests/test_stmex.vhd b/libraries/nexys2/tests/test_stmex.vhd
new file mode 100644 (file)
index 0000000..81b8f5f
--- /dev/null
@@ -0,0 +1,183 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library simulated;
+
+library utility;
+
+
+entity test_stmex is
+end test_stmex;
+
+
+architecture behavior of test_stmex is
+
+       -- Host
+       signal dl_data:  std_logic_vector(7 downto 0);
+       signal ul_count: integer;
+
+       -- STM interface
+       signal ifclk:   std_logic;
+       signal stmen:   std_logic;
+       signal db:      std_logic_vector(7 downto 0);
+       signal flaga:   std_logic;
+       signal flagb:   std_logic;
+       signal fifoadr: std_logic_vector(1 downto 0);
+       signal slrd:    std_logic;
+       signal slwr:    std_logic;
+       signal sloe:    std_logic;
+       signal pktend:  std_logic;
+
+       -- Device download interface
+       signal dl_stb:  std_logic;
+       signal dl_rdy:  std_logic;
+       signal dl_dat:  std_logic_vector(7 downto 0);
+       signal dl_end:  std_logic;
+
+       -- Device upload interface
+       signal ul_stb:  std_logic;
+       signal ul_rdy:  std_logic;
+       signal ul_dat:  std_logic_vector(7 downto 0);
+       signal ul_end:  std_logic;
+
+       -- Internal interface
+       signal int_stb: std_logic;
+       signal int_ack: std_logic;
+       signal int_dat: std_logic_vector(7 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               --dl_rdy <= '1';
+               --ul_stb <= '0';
+
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+               wait until falling_edge(ifclk);
+
+               dl_data <= x"a0"; wait for 0 ns;
+               dl_data <= x"a1"; wait for 0 ns;
+               dl_data <= x"a2"; wait for 0 ns;
+               dl_data <= x"a3"; wait for 0 ns;
+               dl_data <= x"a4"; wait for 0 ns;
+               dl_data <= x"a5"; wait for 0 ns;
+               dl_data <= x"a6"; wait for 0 ns;
+               dl_data <= x"a7"; wait for 0 ns;
+               dl_data <= x"b0"; wait for 0 ns;
+               dl_data <= x"b1"; wait for 0 ns;
+               dl_data <= x"b2"; wait for 0 ns;
+               dl_data <= x"b3"; wait for 0 ns;
+               dl_data <= x"b4"; wait for 0 ns;
+               dl_data <= x"b5"; wait for 0 ns;
+               dl_data <= x"b6"; wait for 0 ns;
+               dl_data <= x"b7"; wait for 0 ns;
+               dl_data <= x"c0"; wait for 0 ns;
+               dl_data <= x"c1"; wait for 0 ns;
+               dl_data <= x"c2"; wait for 0 ns;
+               dl_data <= x"c3"; wait for 0 ns;
+               dl_data <= x"c4"; wait for 0 ns;
+               dl_data <= x"c5"; wait for 0 ns;
+               dl_data <= x"c6"; wait for 0 ns;
+               dl_data <= x"c7"; wait for 0 ns;
+               dl_data <= x"d0"; wait for 0 ns;
+               dl_data <= x"d1"; wait for 0 ns;
+               dl_data <= x"d2"; wait for 0 ns;
+               dl_data <= x"d3"; wait for 0 ns;
+               dl_data <= x"d4"; wait for 0 ns;
+               dl_data <= x"d5"; wait for 0 ns;
+               dl_data <= x"d6"; wait for 0 ns;
+               dl_data <= x"d7"; wait for 0 ns;
+               ul_count <= 32; wait for 0 ns;
+
+               --wait until dl_stb = '1';
+               --wait until falling_edge(ifclk);
+               --wait until falling_edge(ifclk);
+               --wait until falling_edge(ifclk);
+               --wait until rising_edge(ifclk);
+               --wait for 2 ns;
+               --dl_rdy <= '0';
+               --wait until falling_edge(ifclk);
+               --wait until falling_edge(ifclk);
+               --wait until falling_edge(ifclk);
+               --wait until rising_edge(ifclk);
+               --wait for 2 ns;
+               --dl_rdy <= '1';
+
+               wait;
+       end process;
+
+       e_dl_fifo: entity utility.fifo_xclk
+               port map (
+                       head_clk_i => ifclk,
+                       head_stb_i => dl_stb,
+                       head_ack_o => dl_rdy,
+                       head_dat_i => dl_dat,
+
+                       tail_clk_i => ifclk,
+                       tail_stb_o => int_stb,
+                       tail_ack_i => int_ack,
+                       tail_dat_o => int_dat
+               );
+
+       e_ul_fifo: entity utility.fifo_xclk
+               port map (
+                       head_clk_i => ifclk,
+                       head_stb_i => int_stb,
+                       head_ack_o => int_ack,
+                       head_dat_i => int_dat,
+
+                       tail_clk_i => ifclk,
+                       tail_stb_o => ul_stb,
+                       tail_ack_i => ul_rdy,
+                       tail_dat_o => ul_dat
+               );
+
+       e_uut: entity work.stmex_wb
+               port map (
+                       ifclk    => ifclk,
+                       stmen    => stmen,
+                       db       => db,
+                       flaga    => flaga,
+                       flagb    => flagb,
+                       fifoadr  => fifoadr,
+                       slrd     => slrd,
+                       slwr     => slwr,
+                       sloe     => sloe,
+                       pktend   => pktend,
+
+                       dl_stb_o => dl_stb,
+                       dl_rdy_i => dl_rdy,
+                       dl_dat_o => dl_dat,
+                       dl_end_o => open,  -- dl_end,
+
+                       ul_stb_i => ul_stb,
+                       ul_rdy_o => ul_rdy,
+                       ul_dat_i => ul_dat,
+                       ul_end_i => '0'  -- ul_end
+               );
+
+       e_host: entity simulated.stmhost
+               port map (
+                       ifclk         => ifclk,
+                       slcs          => stmen,
+                       flaga         => flaga,
+                       flagb         => flagb,
+                       slrd          => slrd,
+                       slwr          => slwr,
+                       sloe          => sloe,
+                       pktend        => pktend,
+                       fifoadr       => fifoadr,
+                       db            => db,
+
+                       host_dl_data  => dl_data,
+                       host_ul_count => ul_count
+               );
+
+end;
index a7e16c6942841e4f0461cd9a5902f78171c2376c..3709245382f8bfc02c2b2486f689b52ac761cc47 100755 (executable)
@@ -11,3 +11,4 @@ gcc -o digdude digdude.c fileformat.c memctrl.c ec.c $CFLAGS -I $INC -L $LIBDIR
 
 gcc -o jtag jtag.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldmgr -ldjtg
 
+gcc -o stm stm.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldmgr -ldstm
diff --git a/projects/nexys2_host_controller/host/stm.c b/projects/nexys2_host_controller/host/stm.c
new file mode 100644 (file)
index 0000000..d6ae891
--- /dev/null
@@ -0,0 +1,167 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+
+// Digilent SDK
+#include "dpcdecl.h"
+#include "dstm.h"
+#include "dmgr.h"
+
+#include "ec.h"
+
+
+// -----------------------------------------------------------------------------
+
+
+#define SIZE  (65536*32)
+#define COUNT 16
+
+
+// -----------------------------------------------------------------------------
+
+
+void usage(void)
+{
+       fprintf(stderr,
+               "STM test program for Digilent Nexys2\n"
+               "\n"
+               "Usage: stm [options]\n"
+               "\nConnection options:\n"
+               "  -p, --part <name>     Specify device (default: \"Nexys2\")\n"
+               "  -P, --port <portnum>  STM port number (default: 0)\n"
+               "\nOutput options:\n"
+               "  -v, --verbose         Verbose output; -v -v for more\n"
+               "  -q, --quell           Quell progress output; -q -q for less\n"
+               "  -?, --help            Display this message\n"
+               "\n"
+               );
+}
+
+
+int main(int argc, char **argv)
+{
+       // Arguments with defaults
+       char *part = "Nexys2";
+       int32_t port = 0;
+       int verbose = 0;
+       int quell = 0;
+
+       // Options
+       int opt;
+       int option_idx = 0;
+       struct option longopts[] = {
+               // Output options
+               {"help",    no_argument,       NULL, '?'},
+               {"quell",   no_argument,       NULL, 'q'},
+               {"verbose", no_argument,       NULL, 'v'},
+
+               // Connection options
+               {"part",    required_argument, NULL, 'p'},
+               {"port",    required_argument, NULL, 'P'},
+       };
+
+       // Port handle and properties
+       HIF hif = hifInvalid;
+       int32_t port_count;
+       char version[cchVersionMax];
+
+       // -------------------------------------------------------------
+       // Parse arguments
+
+       while ((opt = getopt_long(argc, argv, "?qvp:P:", longopts, &option_idx)) != -1)
+       {
+               switch (opt)
+               {
+               case '?': usage(); exit(0); break;
+               case 'q': quell++; break;
+               case 'v': verbose++; break;
+
+               case 'p': part = optarg; break;
+               case 'P': port = strtol(optarg, NULL, 0); break;
+
+               default:
+                       fprintf(stderr, "Invalid option -%c\n\n", opt);
+                       usage();
+                       exit(1);
+                       break;
+               }
+       }
+
+       // -------------------------------------------------------------
+       // Open session
+
+       EC_FALSE(DstmGetVersion(version));
+       printf("DSTM version: %s\n", version);
+
+       EC_FALSE(DmgrOpen(&hif, part));
+       printf("Opened device \"%s\"\n", part);
+
+       EC_FALSE(DstmGetPortCount(hif, &port_count));
+       printf("Port count: %" PRIi32 "\n", port_count);
+       for (int32_t i = 0; i < port_count; i++)
+       {
+               uint32_t port_properties = 0;
+
+               EC_FALSE(DstmGetPortProperties(hif, i, &port_properties));
+               printf("Port %" PRIi32 " properties: 0x%08" PRIx32 "\n", i, port_properties);
+       }
+
+       EC_FALSE(DstmEnableEx(hif, port));
+       printf("Opened STM port %" PRIi32 "\n", port);
+
+       // -------------------------------------------------------------
+       // Do something interesting
+
+       uint8_t dl_data[SIZE] = {0};
+       uint8_t ul_data[SIZE] = {0};
+       for (size_t i = 0; i < SIZE; i++)
+       {
+               dl_data[i] = (uint8_t)i;
+       }
+
+       for (int i = 0; i < COUNT; i++)
+       {
+               // Separate download/upload call transfer
+               //printf("Downloading\n");
+               //EC_FALSE(DstmIO(hif, dl_data, SIZE, NULL, 0, false));
+               //printf("Uploading\n");
+               //EC_FALSE(DstmIO(hif, NULL, 0, ul_data, SIZE, false));
+
+               // Single call transfer
+               //printf("Transfering\n");
+               EC_FALSE(DstmIOEx(hif, dl_data, SIZE, ul_data, SIZE, false));
+
+               //printf("Checking\n");
+               EC_NZ(memcmp(dl_data, ul_data, SIZE));
+       }
+
+       printf("Correct! Transferred %d bytes %d times.\n", SIZE, COUNT);
+
+       // -------------------------------------------------------------
+       // Close session
+
+       if (hif != hifInvalid)
+       {
+               DstmDisable(hif);
+               DmgrClose(hif);
+               printf("Closed session\n");
+       }
+
+       return 0;
+
+EC_CLEANUP_BEGIN
+       if (hif != hifInvalid)
+       {
+               DstmDisable(hif);
+               DmgrClose(hif);
+       }
+
+       ec_print();
+       return 1;
+EC_CLEANUP_END
+}
+