File: /__w/ctu-can-regression/ctu-can-regression/test/main_tb/agents/memory_bus_agent/mem_bus_agent.vhd
0: --------------------------------------------------------------------------------
1: --
2: -- CTU CAN FD IP Core
3: -- Copyright (C) 2021-2023 Ondrej Ille
4: -- Copyright (C) 2023- Logic Design Services Ltd.s
5: --
6: -- Permission is hereby granted, free of charge, to any person obtaining a copy
7: -- of this VHDL component and associated documentation files (the "Component"),
8: -- to use, copy, modify, merge, publish, distribute the Component for
9: -- non-commercial purposes. Using the Component for commercial purposes is
10: -- forbidden unless previously agreed with Copyright holder.
11: --
12: -- The above copyright notice and this permission notice shall be included in
13: -- all copies or substantial portions of the Component.
14: --
15: -- THE COMPONENT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16: -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17: -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18: -- AUTHORS OR COPYRIGHTHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19: -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20: -- FROM, OUT OF OR IN CONNECTION WITH THE COMPONENT OR THE USE OR OTHER DEALINGS
21: -- IN THE COMPONENT.
22: --
23: -- The CAN protocol is developed by Robert Bosch GmbH and protected by patents.
24: -- Anybody who wants to implement this IP core on silicon has to obtain a CAN
25: -- protocol license from Bosch.
26: --
27: -- -------------------------------------------------------------------------------
28: --
29: -- CTU CAN FD IP Core
30: -- Copyright (C) 2015-2020 MIT License
31: --
32: -- Authors:
33: -- Ondrej Ille <ondrej.ille@gmail.com>
34: -- Martin Jerabek <martin.jerabek01@gmail.com>
35: --
36: -- Project advisors:
37: -- Jiri Novak <jnovak@fel.cvut.cz>
38: -- Pavel Pisa <pisa@cmp.felk.cvut.cz>
39: --
40: -- Department of Measurement (http://meas.fel.cvut.cz/)
41: -- Faculty of Electrical Engineering (http://www.fel.cvut.cz)
42: -- Czech Technical University (http://www.cvut.cz/)
43: --
44: -- Permission is hereby granted, free of charge, to any person obtaining a copy
45: -- of this VHDL component and associated documentation files (the "Component"),
46: -- to deal in the Component without restriction, including without limitation
47: -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
48: -- and/or sell copies of the Component, and to permit persons to whom the
49: -- Component is furnished to do so, subject to the following conditions:
50: --
51: -- The above copyright notice and this permission notice shall be included in
52: -- all copies or substantial portions of the Component.
53: --
54: -- THE COMPONENT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55: -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56: -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57: -- AUTHORS OR COPYRIGHTHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58: -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
59: -- FROM, OUT OF OR IN CONNECTION WITH THE COMPONENT OR THE USE OR OTHER DEALINGS
60: -- IN THE COMPONENT.
61: --
62: -- The CAN protocol is developed by Robert Bosch GmbH and protected by patents.
63: -- Anybody who wants to implement this IP core on silicon has to obtain a CAN
64: -- protocol license from Bosch.
65: --
66: --------------------------------------------------------------------------------
67:
68: --------------------------------------------------------------------------------
69: -- @Purpose:
70: -- Memory bus agent. Configurable over Vunit Communication library.
71: --
72: -- More on Vunit and its communication library can be found at:
73: --- https://vunit.github.io/documentation.html
74: -- https://vunit.github.io/com/user_guide.html
75: --
76: -- Memory bus agent executes accesses on simple RAM-like interface. It
77: -- contains FIFO which buffers the accesses. Following types of accesses are
78: -- supported:
79: -- 1. Non-blocking write access
80: -- 2. Blocking write access
81: -- 3. Read access (always blocking)
82: --
83: -- This means that it is impossible to post several Read accesses and execute
84: -- them at once, because read always has to return value, therefore its
85: -- transaction must elapse.
86: --
87: -- Memory bus agent supports two modes:
88: -- normal mode
89: -- X-mode
90: -- In X-mode control signals are driven to X everywhere apart from setup +
91: -- hold within rising edge of clock and read data are sampled with data
92: -- output delay.
93: --
94: -- Memory Bus agent can be started or stopped via Vunit Communication Library.
95: -- When Memory Bus agent is started, it executes transactions from its
96: -- internal FIFO. When it is stopped, transcations are buffered in FIFO and
97: -- executed once Memory bus agent is started.
98: --
99: --------------------------------------------------------------------------------
100: -- Revision History:
101: -- 19.1.2020 Created file
102: -- 04.2.2021 Adjusted to work without Vunits COM library.
103: --------------------------------------------------------------------------------
104:
105: Library ctu_can_fd_tb;
106: context ctu_can_fd_tb.ieee_context;
107: context ctu_can_fd_tb.tb_common_context;
108:
109: use ctu_can_fd_tb.mem_bus_agent_pkg.all;
110: use ctu_can_fd_tb.tb_shared_vars_pkg.all;
111:
112:
113: entity mem_bus_agent is
114: generic(
115: G_ACCESS_FIFO_DEPTH : natural := 32;
116: G_NUM_SLAVES : natural := 2
117: );
118: port (
119: -- Clock
120: clk : in std_logic;
121:
122: -- Memory bus interface
123: scs : out std_logic_vector(G_NUM_SLAVES - 1 downto 0) := (OTHERS => '0');
124: swr : out std_logic := 'X';
125: srd : out std_logic := 'X';
126: sbe : out std_logic_vector(3 downto 0) := "XXXX";
127: write_data : out std_logic_vector(31 downto 0) := (others => '0');
128: read_data : in std_logic_vector(31 downto 0);
129: address : out std_logic_vector(15 downto 0) := (others => '0')
130: );
131: end entity;
132:
133: architecture tb of mem_bus_agent is
134:
135: type t_mem_bus_access_array is array (0 to G_ACCESS_FIFO_DEPTH - 1)
136: of t_mem_bus_access_item;
137:
138: signal mem_bus_access_fifo : t_mem_bus_access_array;
139: signal fifo_wp : integer := 0;
140: signal fifo_rp : integer := 0;
141:
142: --------------------------------------------------------------------------
143: -- Configuration parameters
144: ---------------------------------------------------------------------------
145: signal mem_bus_agent_ena : boolean := false;
146: signal is_x_mode : boolean := true;
147:
148: -- Only single setup for all input signals
149: signal x_mode_setup : time := 2 ns;
150: signal x_mode_hold : time := 1 ns;
151:
152: -- Output signal is output data only!
153: signal data_out_delay : time := 3 ns;
154:
155: signal period : time := 10 ns;
156:
157: signal read_data_i : std_logic_vector(31 downto 0);
158:
159: signal scs_i : std_logic := '0';
160:
161: -- By default, transactions go to first slave (DUT). This is used in compliance
162: -- tests which only talk to DUT node via Memory bus agent.
163: signal slave_index : natural := 0;
164:
165: signal last_clk_re : time := 0 ns;
166:
167: signal trans_report_en : boolean := true;
168:
169: begin
170:
171: --------------------------------------------------------------------------
172: -- Comunication receiver process
173: ---------------------------------------------------------------------------
174: receiver_proc : process
175: variable cmd : integer;
176: variable reply_code : integer;
177: variable transaction : t_mem_bus_access_item;
178: variable tmp : std_logic_vector(127 downto 0);
179: variable tmp_int : integer;
180: begin
181: receive_start(default_channel, C_MEM_BUS_AGENT_ID);
182:
183: -- Command is sent as message type
184: cmd := com_channel_data.get_msg_code;
185: reply_code := C_REPLY_CODE_OK;
186:
187: case cmd is
188: when MEM_BUS_AGNT_CMD_START =>
189: mem_bus_agent_ena <= true;
190: when MEM_BUS_AGNT_CMD_STOP =>
191: mem_bus_agent_ena <= false;
192:
193: -- For non-blocking writes only post to FIFO, don't wait!
194: when MEM_BUS_AGNT_CMD_WRITE_NON_BLOCKING =>
195: transaction.write := true;
196: transaction.address := com_channel_data.get_param;
197: tmp := com_channel_data.get_param;
198: transaction.byte_enable := tmp(3 DOWNTO 0);
199: transaction.write_data := tmp(35 DOWNTO 4);
200:
201: mem_bus_access_fifo(fifo_wp) <= transaction;
202: wait for 0 ns;
203: fifo_wp <= (fifo_wp + 1) mod G_ACCESS_FIFO_DEPTH;
204:
205: -- For blocking writes wait until FIFO is emptied. This will wait also if
206: -- previous non-blocking transactions were pushed!
207: when MEM_BUS_AGNT_CMD_WRITE_BLOCKING =>
208: transaction.write := true;
209: transaction.address := com_channel_data.get_param;
210: tmp := com_channel_data.get_param;
211: transaction.byte_enable := tmp(3 DOWNTO 0);
212: transaction.write_data := tmp(35 DOWNTO 4);
213:
214: mem_bus_access_fifo(fifo_wp) <= transaction;
215: wait for 0 ns;
216: fifo_wp <= (fifo_wp + 1) mod G_ACCESS_FIFO_DEPTH;
217: wait for 0 ns;
218:
219: wait until (fifo_wp = fifo_rp);
220:
221: -- Reads are always blocking
222: when MEM_BUS_AGNT_CMD_READ =>
223: transaction.write := false;
224: tmp_int := com_channel_data.get_param;
225: transaction.address := com_channel_data.get_param;
226: tmp := com_channel_data.get_param;
227: transaction.byte_enable := tmp(3 DOWNTO 0);
228:
229: mem_bus_access_fifo(fifo_wp) <= transaction;
230: wait for 0 ns;
231: fifo_wp <= (fifo_wp + 1) mod G_ACCESS_FIFO_DEPTH;
232: wait for 0 ns;
233:
234: wait until (fifo_wp = fifo_rp);
235: --info_m("Read data when pushing response: " & to_hstring(read_data_i));
236: com_channel_data.set_param(read_data_i);
237:
238: when MEM_BUS_AGNT_CMD_X_MODE_START =>
239: is_x_mode <= true;
240:
241: when MEM_BUS_AGNT_CMD_X_MODE_STOP =>
242: is_x_mode <= false;
243:
244: when MEM_BUS_AGNT_CMD_SET_X_MODE_SETUP =>
245: x_mode_setup <= com_channel_data.get_param;
246:
247: when MEM_BUS_AGNT_CMD_SET_X_MODE_HOLD =>
248: x_mode_hold <= com_channel_data.get_param;
249:
250: when MEM_BUS_AGNT_CMD_SET_OUTPUT_DELAY =>
251: data_out_delay <= com_channel_data.get_param;
252:
253: when MEM_BUS_AGNT_CMD_WAIT_DONE =>
254: if (fifo_wp /= fifo_rp) then
255: wait until fifo_wp = fifo_rp;
256: end if;
257:
258: when MEM_BUS_AGNT_CMD_SET_SLAVE_INDEX =>
259: slave_index <= com_channel_data.get_param;
260: wait for 0 ns;
261:
262: when MEM_BUS_AGNT_CMD_ENABLE_TRANS_REPORT =>
263: trans_report_en <= true;
264:
265: when MEM_BUS_AGNT_CMD_DISABLE_TRANS_REPORT =>
266: trans_report_en <= false;
267:
268: when others =>
269: info_m("Invalid message type: " & integer'image(cmd));
270: reply_code := C_REPLY_CODE_ERR;
271: end case;
272:
273: receive_finish(default_channel, reply_code);
274: end process;
275:
276: ---------------------------------------------------------------------------
277: -- Measuring period of input clock (for proper calculation in X mode)
278: ---------------------------------------------------------------------------
279: clk_period_meas_proc : process
280: begin
281: wait until rising_edge(clk);
282: period <= NOW - last_clk_re;
283: last_clk_re <= NOW;
284: end process;
285:
286:
287: ---------------------------------------------------------------------------
288: -- Memory bus access process
289: ---------------------------------------------------------------------------
290: mem_bus_access_proc : process
291: variable curr_access : t_mem_bus_access_item;
292:
293: procedure print_write_access(
294: variable mem_access : inout t_mem_bus_access_item
295: ) is
296: variable node_str : string(1 to 10);
297: begin
298: if (slave_index = 0) then
299: node_str := "DUT Node ";
300: else
301: node_str := "TEST Node ";
302: end if;
303:
304: if (trans_report_en) then
305: info_m(MEM_BUS_AGENT_TAG & "Write to: " & node_str &
306: "Address: 0x" & to_hstring(std_logic_vector(to_unsigned(curr_access.address, 32))) &
307: ", Write Data: 0x" & to_hstring(curr_access.write_data) &
308: ", Be: " & to_hstring(curr_access.byte_enable)
309: );
310: end if;
311: end procedure;
312:
313:
314: procedure print_read_access(
315: variable mem_access : inout t_mem_bus_access_item
316: ) is
317: variable node_str : string(1 to 10);
318: begin
319: if (slave_index = 0) then
320: node_str := "DUT Node ";
321: else
322: node_str := "TEST Node ";
323: end if;
324:
325: if (trans_report_en) then
326: info_m(MEM_BUS_AGENT_TAG & "Read from: " & node_str &
327: "Address: 0x" & to_hstring(std_logic_vector(to_unsigned(curr_access.address, 32))) &
328: ", Read Data: 0x" & to_hstring(curr_access.read_data) &
329: ", Be: " & to_hstring(curr_access.byte_enable)
330: );
331: end if;
332: end procedure;
333:
334:
335:
336: procedure drive_access(
337: variable mem_access : inout t_mem_bus_access_item;
338: signal read_data_in : out std_logic_vector(31 downto 0)
339: ) is
340: variable post_re_time, post_re_time_2 : time;
341: begin
342: -- Chip select can't have Xs, otherwise we get dummy transactions!
343: wait until falling_edge(clk);
344: scs_i <= '1';
345:
346: if (is_x_mode) then
347: swr <= 'X';
348: srd <= 'X';
349: write_data <= (OTHERS => 'X');
350: sbe <= "XXXX";
351: address <= (OTHERS => 'X');
352: wait for (period / 2) - x_mode_setup;
353: end if;
354:
355: if (mem_access.write) then
356: swr <= '1';
357: srd <= '0';
358: write_data <= mem_access.write_data;
359: else
360: swr <= '0';
361: srd <= '1';
362: end if;
363:
364: address <= std_logic_vector(to_unsigned(mem_access.address, address'length));
365: sbe <= mem_access.byte_enable;
366:
367: wait until rising_edge(clk);
368:
369: -- If hold time is larger than data output time, first sample output data,
370: -- otherwise first drive X due to end of hold!
371: if (x_mode_hold > data_out_delay) then
372: post_re_time := data_out_delay;
373: post_re_time_2 := x_mode_hold - post_re_time;
374: else
375: post_re_time := x_mode_hold;
376: post_re_time_2 := data_out_delay - post_re_time;
377: end if;
378:
379: if (is_x_mode) then
380: wait for post_re_time;
381:
382: if (post_re_time = x_mode_hold) then
383: scs_i <= '0';
384: swr <= 'X';
385: srd <= 'X';
386: write_data <= (OTHERS => 'X');
387: sbe <= "XXXX";
388: address <= (OTHERS => 'X');
389: else
390: read_data_in <= read_data;
391: wait for 0 ns;
392: end if;
393:
394: wait for post_re_time_2;
395:
396: if (post_re_time = x_mode_hold) then
397: read_data_in <= read_data;
398: else
399: scs_i <= '0';
400: swr <= 'X';
401: srd <= 'X';
402: write_data <= (OTHERS => 'X');
403: sbe <= "XXXX";
404: address <= (OTHERS => 'X');
405: end if;
406:
407: -- Sample data without waiting, it should be available immediately!
408: else
409: wait for 1 ps; -- To avoid possible delta cycles
410: read_data_in <= read_data;
411: wait for (period / 2) - 2 ps; -- This will end up just before next falling edge!
412: swr <= '0';
413: srd <= '0';
414: scs_i <= '0';
415: address <= (OTHERS => '0');
416: write_data <= (OTHERS => '0');
417: sbe <= (OTHERS => '0');
418: end if;
419: end procedure;
420:
421: begin
422: if (mem_bus_agent_ena) then
423: while (true) loop
424: if (not mem_bus_agent_ena) then
425: exit;
426: end if;
427:
428: -- There is something in FIFO -> do memory access
429: if (fifo_rp /= fifo_wp) then
430: curr_access := mem_bus_access_fifo(fifo_rp);
431:
432: if (curr_access.write) then
433: print_write_access(curr_access);
434: end if;
435: drive_access(curr_access, read_data_i);
436:
437: wait for 0 ns;
438: curr_access.read_data := read_data_i;
439: if (curr_access.write = false) then
440: print_read_access(curr_access);
441: end if;
442:
443: fifo_rp <= (fifo_rp + 1) mod G_ACCESS_FIFO_DEPTH;
444: wait for 0 ns;
445: else
446: wait until fifo_rp /= fifo_wp or mem_bus_agent_ena=false;
447: end if;
448: end loop;
449: else
450: wait until mem_bus_agent_ena;
451: end if;
452: end process;
453:
454: ---------------------------------------------------------------------------
455: -- Propagate chip select to slave which is selected
456: ---------------------------------------------------------------------------
457: cs_gen : for i in 0 to G_NUM_SLAVES - 1 generate
458: scs(i) <= scs_i when (slave_index = i) else
459: '0';
460: end generate;
461:
462: end architecture;