From: rs <> Date: Tue, 11 Nov 2025 21:40:00 +0000 (-0600) Subject: Add STM interface and host test program X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=1fc546a2dc2a9dc6ed5fc88b67b4613852888e7e;p=vhdl Add STM interface and host test program --- diff --git a/.gitignore b/.gitignore index b0d8c71..889539a 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 0000000..bb066af --- /dev/null +++ b/libraries/nexys2/stmex_wb.vhd @@ -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 index 0000000..5944de4 --- /dev/null +++ b/libraries/nexys2/tests/nexys2_stm.vhd @@ -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 index 0000000..d38660c --- /dev/null +++ b/libraries/nexys2/tests/test_nexys2stm.vhd @@ -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 index 0000000..81b8f5f --- /dev/null +++ b/libraries/nexys2/tests/test_stmex.vhd @@ -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; diff --git a/projects/nexys2_host_controller/host/build.sh b/projects/nexys2_host_controller/host/build.sh index a7e16c6..3709245 100755 --- a/projects/nexys2_host_controller/host/build.sh +++ b/projects/nexys2_host_controller/host/build.sh @@ -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 index 0000000..d6ae891 --- /dev/null +++ b/projects/nexys2_host_controller/host/stm.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +// 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 Specify device (default: \"Nexys2\")\n" + " -P, --port 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 +} +