Library of reusable VHDL components
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
13 KiB

  1. library ieee;
  2. use ieee.std_logic_1164.all;
  3. use ieee.numeric_std.all;
  4. --+ including vhdl 2008 libraries
  5. --+ These lines can be commented out when using
  6. --+ a simulator with built-in VHDL 2008 support
  7. --library ieee_proposed;
  8. -- use ieee_proposed.standard_additions.all;
  9. -- use ieee_proposed.std_logic_1164_additions.all;
  10. -- use ieee_proposed.numeric_std_additions.all;
  11. library osvvm;
  12. use osvvm.RandomPkg.all;
  13. library libvhdl;
  14. use libvhdl.AssertP.all;
  15. use libvhdl.SimP.all;
  16. use libvhdl.QueueP.all;
  17. entity SpiT is
  18. end entity SpiT;
  19. architecture sim of SpiT is
  20. component SpiMasterE is
  21. generic (
  22. G_DATA_WIDTH : positive := 8;
  23. G_DATA_DIR : natural range 0 to 1 := 0;
  24. G_SPI_CPOL : natural range 0 to 1 := 0;
  25. G_SPI_CPHA : natural range 0 to 1 := 0;
  26. G_SCLK_DIVIDER : positive range 6 to positive'high := 10
  27. );
  28. port (
  29. --+ system if
  30. Reset_n_i : in std_logic;
  31. Clk_i : in std_logic;
  32. --+ SPI slave if
  33. SpiSclk_o : out std_logic;
  34. SpiSte_o : out std_logic;
  35. SpiMosi_o : out std_logic;
  36. SpiMiso_i : in std_logic;
  37. --+ local VAI if
  38. Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0);
  39. DataValid_i : in std_logic;
  40. DataAccept_o : out std_logic;
  41. Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0);
  42. DataValid_o : out std_logic;
  43. DataAccept_i : in std_logic
  44. );
  45. end component SpiMasterE;
  46. component SpiSlaveE is
  47. generic (
  48. G_DATA_WIDTH : positive := 8;
  49. G_DATA_DIR : natural range 0 to 1 := 0;
  50. G_SPI_CPOL : natural range 0 to 1 := 0;
  51. G_SPI_CPHA : natural range 0 to 1 := 0
  52. );
  53. port (
  54. --+ system if
  55. Reset_n_i : in std_logic;
  56. Clk_i : in std_logic;
  57. --+ SPI slave if
  58. SpiSclk_i : in std_logic;
  59. SpiSte_i : in std_logic;
  60. SpiMosi_i : in std_logic;
  61. SpiMiso_o : out std_logic;
  62. --+ local VAI if
  63. Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0);
  64. DataValid_i : in std_logic;
  65. DataAccept_o : out std_logic;
  66. Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0);
  67. DataValid_o : out std_logic;
  68. DataAccept_i : in std_logic
  69. );
  70. end component SpiSlaveE;
  71. --* testbench global clock period
  72. constant C_PERIOD : time := 5 ns;
  73. --* SPI data transfer data width
  74. constant C_DATA_WIDTH : natural := 8;
  75. --* testbench global clock
  76. signal s_clk : std_logic := '0';
  77. --* testbench global reset
  78. signal s_reset_n : std_logic := '0';
  79. --* SPI mode range subtype
  80. subtype t_spi_mode is natural range 0 to 3;
  81. --+ test done array with entry for each test
  82. signal s_test_done : boolean_vector(t_spi_mode'low to 4*t_spi_mode'high+3) := (others => false);
  83. begin
  84. --* testbench global clock
  85. s_clk <= not(s_clk) after C_PERIOD/2 when not(and_reduce(s_test_done)) else '0';
  86. --* testbench global reset
  87. s_reset_n <= '1' after 100 ns;
  88. --* Generate tests for both direction
  89. DataDirectionG : for direction in 0 to 1 generate
  90. --* Generate SpiMasterE tests for all 4 modes
  91. SpiMastersG : for mode in t_spi_mode'low to t_spi_mode'high generate
  92. signal s_sclk : std_logic;
  93. signal s_ste : std_logic;
  94. signal s_mosi : std_logic;
  95. signal s_miso : std_logic;
  96. signal s_din : std_logic_vector(C_DATA_WIDTH-1 downto 0);
  97. signal s_din_valid : std_logic;
  98. signal s_din_accept : std_logic;
  99. signal s_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0);
  100. signal s_dout_valid : std_logic;
  101. signal s_dout_accept : std_logic;
  102. shared variable sv_mosi_queue : t_list_queue;
  103. shared variable sv_miso_queue : t_list_queue;
  104. begin
  105. QueueInitP : process is
  106. begin
  107. sv_mosi_queue.init(32);
  108. sv_miso_queue.init(32);
  109. wait;
  110. end process QueueInitP;
  111. --* Stimuli generator and BFM for the valid-accept interface
  112. --* on the local data input of the DUT
  113. --*
  114. --* Generates random stimuli and serves it to the
  115. --* valid-accept interface at the input of the DUT
  116. --*
  117. --* The stimuli data is also pushed into the mosi queue
  118. --* which serves as simple abstract reference model
  119. --* of the SPI transmit (master -> slave) channel
  120. SpiMasterStimP : process is
  121. variable v_random : RandomPType;
  122. begin
  123. v_random.InitSeed(v_random'instance_name);
  124. s_din_valid <= '0';
  125. s_din <= (others => '0');
  126. wait until s_reset_n = '1';
  127. for i in 0 to integer'(2**C_DATA_WIDTH-1) loop
  128. s_din <= v_random.RandSlv(C_DATA_WIDTH);
  129. s_din_valid <= '1';
  130. wait until rising_edge(s_clk) and s_din_accept = '1';
  131. s_din_valid <= '0';
  132. sv_mosi_queue.push(s_din);
  133. wait until rising_edge(s_clk);
  134. end loop;
  135. wait;
  136. end process SpiMasterStimP;
  137. --* DUT: SpiMasterE component
  138. i_SpiMasterE : SpiMasterE
  139. generic map (
  140. G_DATA_WIDTH => C_DATA_WIDTH,
  141. G_DATA_DIR => direction,
  142. G_SPI_CPOL => mode / 2,
  143. G_SPI_CPHA => mode mod 2,
  144. G_SCLK_DIVIDER => 10
  145. )
  146. port map (
  147. --+ system if
  148. Reset_n_i => s_reset_n,
  149. Clk_i => s_clk,
  150. --+ SPI slave if
  151. SpiSclk_o => s_sclk,
  152. SpiSte_o => s_ste,
  153. SpiMosi_o => s_mosi,
  154. SpiMiso_i => s_miso,
  155. --+ local VAI if
  156. Data_i => s_din,
  157. DataValid_i => s_din_valid,
  158. DataAccept_o => s_din_accept,
  159. Data_o => s_dout,
  160. DataValid_o => s_dout_valid,
  161. DataAccept_i => s_dout_accept
  162. );
  163. --* Checker and BFM for the valid-accept interface
  164. --* on the local data output of the DUT
  165. --*
  166. --* Reads the output of the DUT and compares it to
  167. --* data popped from the miso queue which serves as
  168. --* simple abstract reference model of the SPI receive
  169. --* (slave -> master) channel
  170. SpiMasterCheckP : process is
  171. variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  172. begin
  173. s_dout_accept <= '0';
  174. wait until s_reset_n = '1';
  175. for i in 0 to integer'(2**C_DATA_WIDTH-1) loop
  176. wait until rising_edge(s_clk) and s_dout_valid = '1';
  177. s_dout_accept <= '1';
  178. sv_miso_queue.pop(v_queue_data);
  179. assert_equal(s_dout, v_queue_data);
  180. wait until rising_edge(s_clk);
  181. s_dout_accept <= '0';
  182. end loop;
  183. report "INFO: SpiMaster (direction=" & to_string(direction) & ", mode=" & to_string(mode) & ") test successfully";
  184. s_test_done(mode+direction*4) <= true;
  185. wait;
  186. end process SpiMasterCheckP;
  187. --* Stimuli generator and BFM for the SPI slave
  188. --* interface on the SPI miso input of the DUT
  189. --*
  190. --* Generates random stimuli and serves it to the
  191. --* SPI interface at the input of the DUT
  192. --*
  193. --* The stimuli data is also pushed into the miso queue
  194. --* which serves as simple abstract reference model
  195. --* of the SPI receive (slave -> master) channel
  196. --*
  197. --* Furthermore the data received by the SPI slave BFM
  198. --* is checked against data popped from the mosi queue
  199. --* which serves as simple abstract reference model of
  200. --* the SPI receive (master -> slave) channel
  201. SpiSlaveP : process is
  202. variable v_send_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  203. variable v_receive_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  204. variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  205. variable v_random : RandomPType;
  206. begin
  207. v_random.InitSeed(v_random'instance_name);
  208. s_miso <= 'Z';
  209. wait until s_reset_n = '1';
  210. for i in 0 to integer'(2**C_DATA_WIDTH-1) loop
  211. v_send_data := v_random.RandSlv(C_DATA_WIDTH);
  212. sv_miso_queue.push(v_send_data);
  213. spi_slave (data_in => v_send_data,
  214. data_out => v_receive_data,
  215. sclk => s_sclk,
  216. ste => s_ste,
  217. mosi => s_mosi,
  218. miso => s_miso,
  219. dir => direction,
  220. cpol => mode / 2,
  221. cpha => mode mod 2
  222. );
  223. sv_mosi_queue.pop(v_queue_data);
  224. assert_equal(v_receive_data, v_queue_data);
  225. end loop;
  226. wait;
  227. end process SpiSlaveP;
  228. end generate SpiMastersG;
  229. --* Generate SpiMasterE tests for all 4 modes
  230. SpiSlavesG : for mode in t_spi_mode'low to t_spi_mode'high generate
  231. signal s_sclk : std_logic;
  232. signal s_ste : std_logic;
  233. signal s_mosi : std_logic;
  234. signal s_miso : std_logic;
  235. signal s_din : std_logic_vector(C_DATA_WIDTH-1 downto 0);
  236. signal s_din_valid : std_logic;
  237. signal s_din_accept : std_logic;
  238. signal s_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0);
  239. signal s_dout_valid : std_logic;
  240. signal s_dout_accept : std_logic;
  241. shared variable sv_mosi_queue : t_list_queue;
  242. shared variable sv_miso_queue : t_list_queue;
  243. begin
  244. QueueInitP : process is
  245. begin
  246. sv_mosi_queue.init(32);
  247. sv_miso_queue.init(32);
  248. wait;
  249. end process QueueInitP;
  250. --* Unit test of spi master procedure, checks all combinations
  251. --* of cpol & cpha against spi slave procedure
  252. SpiMasterP : process is
  253. variable v_send_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  254. variable v_receive_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  255. variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  256. variable v_random : RandomPType;
  257. begin
  258. v_random.InitSeed(v_random'instance_name);
  259. s_sclk <= '1';
  260. s_ste <= '1';
  261. s_mosi <= '1';
  262. wait until s_reset_n = '1';
  263. for i in 0 to integer'(2**C_DATA_WIDTH-1) loop
  264. v_send_data := v_random.RandSlv(C_DATA_WIDTH);
  265. sv_mosi_queue.push(v_send_data);
  266. spi_master (data_in => v_send_data,
  267. data_out => v_receive_data,
  268. sclk => s_sclk,
  269. ste => s_ste,
  270. mosi => s_mosi,
  271. miso => s_miso,
  272. dir => direction,
  273. cpol => mode / 2,
  274. cpha => mode mod 2,
  275. period => C_PERIOD * 10
  276. );
  277. sv_miso_queue.pop(v_queue_data);
  278. assert_equal(v_receive_data, v_queue_data);
  279. end loop;
  280. report "INFO: SpiSlave (direction=" & to_string(direction) & ", mode=" & to_string(mode) & ") test successfully";
  281. s_test_done(mode+8+direction*4) <= true;
  282. wait;
  283. end process SpiMasterP;
  284. SpiSlaveStimP : process is
  285. variable v_random : RandomPType;
  286. begin
  287. v_random.InitSeed(v_random'instance_name);
  288. s_din_valid <= '0';
  289. s_din <= (others => '0');
  290. wait until s_reset_n = '1';
  291. for i in 0 to integer'(2**C_DATA_WIDTH-1) loop
  292. s_din <= v_random.RandSlv(C_DATA_WIDTH);
  293. s_din_valid <= '1';
  294. wait until rising_edge(s_clk) and s_din_accept = '1';
  295. s_din_valid <= '0';
  296. sv_miso_queue.push(s_din);
  297. wait until rising_edge(s_clk) and s_dout_valid = '1';
  298. end loop;
  299. wait;
  300. end process SpiSlaveStimP;
  301. i_SpiSlaveE : SpiSlaveE
  302. generic map (
  303. G_DATA_WIDTH => C_DATA_WIDTH,
  304. G_DATA_DIR => direction,
  305. G_SPI_CPOL => mode / 2,
  306. G_SPI_CPHA => mode mod 2
  307. )
  308. port map (
  309. --+ system if
  310. Reset_n_i => s_reset_n,
  311. Clk_i => s_clk,
  312. --+ SPI slave if
  313. SpiSclk_i => s_sclk,
  314. SpiSte_i => s_ste,
  315. SpiMosi_i => s_mosi,
  316. SpiMiso_o => s_miso,
  317. --+ local VAI if
  318. Data_i => s_din,
  319. DataValid_i => s_din_valid,
  320. DataAccept_o => s_din_accept,
  321. Data_o => s_dout,
  322. DataValid_o => s_dout_valid,
  323. DataAccept_i => s_dout_accept
  324. );
  325. SpiSlaveCheckP : process is
  326. variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0');
  327. begin
  328. s_dout_accept <= '0';
  329. wait until s_reset_n = '1';
  330. for i in 0 to integer'(2**C_DATA_WIDTH-1) loop
  331. wait until rising_edge(s_clk) and s_dout_valid = '1';
  332. s_dout_accept <= '1';
  333. sv_mosi_queue.pop(v_queue_data);
  334. assert_equal(s_dout, v_queue_data);
  335. wait until rising_edge(s_clk);
  336. s_dout_accept <= '0';
  337. end loop;
  338. wait;
  339. end process SpiSlaveCheckP;
  340. end generate SpiSlavesG;
  341. end generate DataDirectionG;
  342. end architecture sim;