diff --git a/vunit/vhdl/verification_components/src/spi_master.vhd b/vunit/vhdl/verification_components/src/spi_master.vhd new file mode 100644 index 000000000..d9363c683 --- /dev/null +++ b/vunit/vhdl/verification_components/src/spi_master.vhd @@ -0,0 +1,115 @@ +library ieee; +use ieee.std_logic_1164.all; + +use work.com_pkg.net; +use work.com_pkg.receive; +use work.com_pkg.reply; +use work.com_types_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +use work.sync_pkg.all; +use work.spi_pkg.all; +use work.queue_pkg.all; +use work.sync_pkg.all; +use work.print_pkg.all; +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.log_handler_pkg.all; + +entity spi_master is +generic ( + spi : spi_master_t +); +port ( + sclk : out std_logic := spi.p_cpol_mode; + mosi : out std_logic := spi.p_idle_state; + miso : in std_logic +); +end entity; + +architecture a of spi_master is + + constant din_queue : queue_t := new_queue; + +begin + + main : process + + procedure spi_transaction( + dout : std_logic_vector; + frequency : integer; + signal sclk : out std_logic; + signal mosi : out std_logic; + signal miso : in std_logic + ) is + constant half_bit_time : time := (10**9 / (frequency*2)) * 1 ns; + variable din : std_logic_vector(dout'length-1 downto 0); + variable clk : std_logic := spi.p_cpol_mode; + begin + debug("Transmitting " & to_string(dout)); + sclk <= clk; + mosi <= dout(dout'length-1); + if (spi.p_cpha_mode = '0') then + wait for half_bit_time; + end if; + clk := not clk; + sclk <= clk; + if (spi.p_cpha_mode = '0') then + din(dout'length-1) := miso; + end if; + for b in dout'length-2 downto 0 loop + wait for half_bit_time; + clk := not clk; + sclk <= clk; + if (spi.p_cpha_mode = '0') then + mosi <= dout(b); + else + din(b) := miso; + end if; + wait for half_bit_time; + clk := not clk; + sclk <= clk; + if (spi.p_cpha_mode = '1') then + mosi <= dout(b); + else + din(b) := miso; + end if; + end loop; + wait for half_bit_time; + sclk <= spi.p_cpol_mode; + push_std_ulogic_vector(din_queue, din); + end procedure; + + variable query_msg : msg_t; + variable reply_msg : msg_t; + variable frequency : natural := spi.p_frequency; + variable msg_type : msg_type_t; + variable din : std_logic_vector(7 downto 0); + + begin + + receive(net, spi.p_actor, query_msg); + msg_type := message_type(query_msg); + + handle_sync_message(net, msg_type, query_msg); + + if msg_type = stream_push_msg then + spi_transaction(pop_std_ulogic_vector(query_msg), frequency, sclk, mosi, miso); + elsif msg_type = stream_pop_msg then + if (length(din_queue) > 0) then + reply_msg := new_msg; + push_std_ulogic_vector(reply_msg, pop_std_ulogic_vector(din_queue)); + push_boolean(reply_msg, false); + reply(net, query_msg, reply_msg); + else + unexpected_msg_type(msg_type); + end if; + elsif msg_type = spi_set_frequency_msg then + frequency := pop(query_msg); + else + unexpected_msg_type(msg_type); + end if; + + end process; + +end architecture; diff --git a/vunit/vhdl/verification_components/src/spi_pkg.vhd b/vunit/vhdl/verification_components/src/spi_pkg.vhd new file mode 100644 index 000000000..0029ff0d3 --- /dev/null +++ b/vunit/vhdl/verification_components/src/spi_pkg.vhd @@ -0,0 +1,132 @@ +library ieee; +use ieee.std_logic_1164.all; + +context work.com_context; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; +use work.sync_pkg.all; +use work.integer_vector_ptr_pkg.all; +use work.queue_pkg.all; + +package spi_pkg is + + type spi_master_t is record + p_actor : actor_t; + p_cpol_mode : std_logic; + p_cpha_mode : std_logic; + p_idle_state : std_logic; + p_frequency : natural; + end record; + + type spi_slave_t is record + p_actor : actor_t; + p_cpol_mode : std_logic; + p_cpha_mode : std_logic; + end record; + + procedure set_frequency( + signal net : inout network_t; + spi_master : spi_master_t; + frequency : natural + ); + + constant default_cpol_mode : std_logic := '0'; + constant default_cpha_mode : std_logic := '0'; + constant default_idle_state : std_logic := '0'; + constant default_frequency : natural := 1000000; + + impure function new_spi_master( + cpol_mode : std_logic := default_cpol_mode; + cpha_mode : std_logic := default_cpha_mode; + idle_state : std_logic := default_idle_state; + initial_frequency : natural := default_frequency + ) return spi_master_t; + + impure function new_spi_slave( + cpol_mode : std_logic := default_cpol_mode; + cpha_mode : std_logic := default_cpha_mode + ) return spi_slave_t; + + impure function as_stream(spi_master : spi_master_t) return stream_master_t; + impure function as_stream(spi_master : spi_master_t) return stream_slave_t; + impure function as_stream(spi_slave : spi_slave_t) return stream_master_t; + impure function as_stream(spi_slave : spi_slave_t) return stream_slave_t; + impure function as_sync (spi_master : spi_master_t) return sync_handle_t; + impure function as_sync (spi_slave : spi_slave_t) return sync_handle_t; + + constant spi_set_frequency_msg : msg_type_t := new_msg_type("spi set frequency"); + +end package spi_pkg; + +package body spi_pkg is + + impure function new_spi_master( + cpol_mode : std_logic := default_cpol_mode; + cpha_mode : std_logic := default_cpha_mode; + idle_state : std_logic := default_idle_state; + initial_frequency : natural := default_frequency + ) return spi_master_t is + begin + return ( + p_actor => new_actor, + p_cpol_mode => cpol_mode, + p_cpha_mode => cpha_mode, + p_idle_state => default_idle_state, + p_frequency => initial_frequency + ); + end function new_spi_master; + + impure function new_spi_slave( + cpol_mode : std_logic := default_cpol_mode; + cpha_mode : std_logic := default_cpha_mode + ) return spi_slave_t is + begin + return ( + p_actor => new_actor, + p_cpol_mode => cpol_mode, + p_cpha_mode => cpha_mode + ); + end function new_spi_slave; + + impure function as_stream(spi_master : spi_master_t) return stream_master_t is + begin + return stream_master_t'(p_actor => spi_master.p_actor); + end function as_stream; + + impure function as_stream(spi_master : spi_master_t) return stream_slave_t is + begin + return stream_slave_t'(p_actor => spi_master.p_actor); + end function as_stream; + + impure function as_stream(spi_slave : spi_slave_t) return stream_master_t is + begin + return stream_master_t'(p_actor => spi_slave.p_actor); + end function as_stream; + + impure function as_stream(spi_slave : spi_slave_t) return stream_slave_t is + begin + return stream_slave_t'(p_actor => spi_slave.p_actor); + end function as_stream; + + impure function as_sync(spi_master : spi_master_t) return sync_handle_t is + begin + return spi_master.p_actor; + end function as_sync; + + impure function as_sync(spi_slave : spi_slave_t) return sync_handle_t is + begin + return spi_slave.p_actor; + end function as_sync; + + procedure set_frequency( + signal net : inout network_t; + spi_master : spi_master_t; + frequency : natural + ) is + variable msg : msg_t := new_msg(spi_set_frequency_msg); + begin + push(msg, frequency); + send(net,spi_master.p_actor,msg); + end procedure set_frequency; + +end package body spi_pkg; diff --git a/vunit/vhdl/verification_components/src/spi_slave.vhd b/vunit/vhdl/verification_components/src/spi_slave.vhd new file mode 100644 index 000000000..2b0a76aa1 --- /dev/null +++ b/vunit/vhdl/verification_components/src/spi_slave.vhd @@ -0,0 +1,108 @@ +library ieee; +use ieee.std_logic_1164.all; + +use work.com_pkg.net; +use work.com_pkg.receive; +use work.com_pkg.reply; +use work.com_types_pkg.all; +use work.stream_slave_pkg.all; +use work.spi_pkg.all; +use work.queue_pkg.all; +use work.print_pkg.all; +use work.log_levels_pkg.all; +use work.logger_pkg.all; +use work.log_handler_pkg.all; + +entity spi_slave is +generic ( + spi : spi_slave_t +); +port ( + sclk : in std_logic := spi.p_cpol_mode; + ss_n : in std_logic := '0'; + mosi : in std_logic; + miso : out std_logic +); +end entity; + +architecture a of spi_slave is + + constant din_queue : queue_t := new_queue; + signal local_event : std_logic := '0'; + +begin + + main : process + + variable reply_msg, query_msg : msg_t; + variable msg_type : msg_type_t; + + begin + + receive(net, spi.p_actor, query_msg); + msg_type := message_type(query_msg); + + if msg_type = stream_pop_msg then + reply_msg := new_msg; + if not (length(din_queue) > 0) then + wait on local_event until length(din_queue) > 0; + end if; + push_std_ulogic_vector(reply_msg, pop_std_ulogic_vector(din_queue)); + push_boolean(reply_msg, false); + reply(net, query_msg, reply_msg); + else + unexpected_msg_type(msg_type); + end if; + + end process; + + recv : process + + procedure spi_transaction( + variable data : out std_logic_vector; + signal sclk : in std_logic; + signal mosi : in std_logic + ) is + variable din_vector : std_logic_vector(7 downto 0); + variable bit_count : natural := 7; + begin + while (ss_n = '0') loop + if ((spi.p_cpha_mode = '0') and (spi.p_cpol_mode = '0')) then + wait until rising_edge(sclk) or ss_n = '1'; wait for 1 ps; + elsif ((spi.p_cpha_mode = '0') and (spi.p_cpol_mode = '1')) then + wait until falling_edge(sclk) or ss_n = '1'; wait for 1 ps; + elsif ((spi.p_cpha_mode = '1') and (spi.p_cpol_mode = '0')) then + wait until falling_edge(sclk) or ss_n = '1'; wait for 1 ps; + elsif ((spi.p_cpha_mode = '1') and (spi.p_cpol_mode = '1')) then + wait until rising_edge(sclk) or ss_n = '1'; wait for 1 ps; + end if; + if (ss_n = '0') then + din_vector(bit_count) := mosi; + if (bit_count = 0) then + bit_count := 7; + debug("Received " & to_string(din_vector)); + push_std_ulogic_vector(din_queue, din_vector); + else + bit_count := bit_count-1; + end if; + end if; + end loop; + end procedure; + + variable data : std_logic_vector(7 downto 0); + + begin + + wait until ss_n = '0'; + spi_transaction(data, sclk, mosi); + local_event <= '1'; + wait for 1 fs; + local_event <= '0'; + wait for 1 fs; + + end process; + + -- Data loopback + miso <= mosi; + +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_spi.vhd b/vunit/vhdl/verification_components/test/tb_spi.vhd new file mode 100644 index 000000000..afe0e05c1 --- /dev/null +++ b/vunit/vhdl/verification_components/test/tb_spi.vhd @@ -0,0 +1,127 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +context work.data_types_context; +use work.spi_pkg.all; +use work.sync_pkg.all; +use work.stream_master_pkg.all; +use work.stream_slave_pkg.all; + +entity tb_spi is + generic (runner_cfg : string); +end entity tb_spi; + +architecture a of tb_spi is + + constant master_spi : spi_master_t := new_spi_master; + constant master_stream : stream_master_t := as_stream(master_spi); + + constant slave_spi : spi_slave_t := new_spi_slave; + constant slave_stream : stream_slave_t := as_stream(slave_spi); + + signal sclk : std_logic; + signal ss_n : std_logic; + signal mosi : std_logic; + signal miso : std_logic; + +begin + + main : process + variable data : std_logic_vector( 7 downto 0); + variable reference_queue : queue_t := new_queue; + variable reference : stream_reference_t; + + procedure test_frequency(frequency : natural) is + variable start : time; + variable got, expected : time; + begin + set_frequency(net, master_spi, frequency); + wait for 10 ps; + start := now; + ss_n <= '0'; + push_stream(net, master_stream, x"77"); + wait_until_idle(net, as_sync(master_spi)); + ss_n <= '1'; + check_stream(net, slave_stream, x"77"); + got := now - start; + expected := (8 * (1 sec)) / (frequency); + check(abs (got - expected) <= 10 ns, + "Unexpected frequency; got " & to_string(got) & " expected " & to_string(expected)); + wait for 10 ps; + end procedure; + + begin + + test_runner_setup(runner, runner_cfg); + ss_n <= '1'; + wait for 10 ps; + + if run("test single push and pop") then + ss_n <= '0'; + push_stream(net, master_stream, x"77"); + wait_until_idle(net, as_sync(master_spi)); + wait for 10 ps; + ss_n <= '1'; + pop_stream(net, slave_stream, data); + check_equal(data, std_logic_vector'(x"77"), "pop stream data"); + wait for 10 ps; + + elsif run("test pop before push") then + for i in 0 to 7 loop + pop_stream(net, slave_stream, reference); + push(reference_queue, reference); + end loop; + + for i in 0 to 7 loop + push_stream(net, master_stream, + std_logic_vector(to_unsigned(i+1, data'length))); + end loop; + + ss_n <= '0'; + wait_until_idle(net, as_sync(master_spi)); + wait for 10 ps; + ss_n <= '1'; + wait for 10 ps; + for i in 0 to 7 loop + reference := pop(reference_queue); + await_pop_stream_reply(net, reference, data); + check_equal(data, to_unsigned(i+1, data'length)); + end loop; + + elsif run("test frequency") then + test_frequency(1000000); + test_frequency(4000000); + test_frequency(8000000); + end if; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 ms); + + spi_master_inst: entity work.spi_master + generic map ( + spi => master_spi + ) + port map ( + sclk => sclk, + mosi => mosi, + miso => miso + ); + + spi_slave_inst: entity work.spi_slave + generic map ( + spi => slave_spi + ) + port map ( + sclk => sclk, + ss_n => ss_n, + mosi => mosi, + miso => miso + ); + + +end architecture a; \ No newline at end of file