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.

424 lines
14 KiB

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