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.

440 lines
14 KiB

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