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.

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