--##############################################################################
|
--##############################################################################
|
-- Simulation test bench -- not synthesizable.
|
-- Simulation test bench -- not synthesizable.
|
--
|
--
|
-- Simulates the MCU core connected to a simulated external static RAM on a
|
-- Simulates the MCU core connected to a simulated external static RAM on a
|
-- 16-bit bus, plus an optional 8-bit static ROM. This setup is more or less
|
-- 16-bit bus, plus an optional 8-bit static ROM. This setup is more or less
|
-- that of develoment board DE-1 from Terasic.
|
-- that of develoment board DE-1 from Terasic.
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
-- Console logging:
|
-- Console logging:
|
--
|
--
|
-- Console output (at addresses compatible to Plasma's) is logged to text file
|
-- Console output (at addresses compatible to Plasma's) is logged to text file
|
-- "hw_sim_console_log.txt".
|
-- "hw_sim_console_log.txt".
|
--
|
--
|
-- IMPORTANT: The code that echoes UART TX data to the simulation console does
|
-- IMPORTANT: The code that echoes UART TX data to the simulation console does
|
-- line buffering; it will not print anything until it gets a CR (0x0d), and
|
-- line buffering; it will not print anything until it gets a CR (0x0d), and
|
-- will ifnore LFs (0x0a). Bear this in mind if you see no output when you
|
-- will ifnore LFs (0x0a). Bear this in mind if you see no output when you
|
-- expect it.
|
-- expect it.
|
--
|
--
|
-- Console logging is done by monitoring CPU writes to the UART, NOT by looking
|
-- Console logging is done by monitoring CPU writes to the UART, NOT by looking
|
-- at the TxD pin. It will NOT catch baud-related problems, etc.
|
-- at the TxD pin. It will NOT catch baud-related problems, etc.
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
-- WARNING: Will only work on Modelsim; uses custom library SignalSpy.
|
-- WARNING: Will only work on Modelsim; uses custom library SignalSpy.
|
--##############################################################################
|
--##############################################################################
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_arith.all;
|
use ieee.std_logic_arith.all;
|
use ieee.std_logic_unsigned.all;
|
use ieee.std_logic_unsigned.all;
|
use std.textio.all;
|
use std.textio.all;
|
|
|
use work.mips_pkg.all;
|
use work.mips_pkg.all;
|
use work.mips_tb_pkg.all;
|
use work.mips_tb_pkg.all;
|
use work.sim_params_pkg.all;
|
use work.sim_params_pkg.all;
|
use work.txt_util.all;
|
use work.txt_util.all;
|
|
|
entity mips_tb is
|
entity mips_tb is
|
end;
|
end;
|
|
|
|
|
architecture testbench of mips_tb is
|
architecture testbench of mips_tb is
|
|
|
-- NOTE: simulation parameters are defined in sim_params_pkg
|
-- NOTE: simulation parameters are defined in sim_params_pkg
|
|
|
|
|
-- External SRAM and interface signals -----------------------------------------
|
-- External SRAM and interface signals -----------------------------------------
|
|
|
signal sram1 : t_sram := ( others => X"00");
|
signal sram1 : t_sram := ( others => X"00");
|
signal sram0 : t_sram := ( others => X"00");
|
signal sram0 : t_sram := ( others => X"00");
|
|
|
signal sram_chip_addr : std_logic_vector(SRAM_ADDR_SIZE downto 1);
|
signal sram_chip_addr : std_logic_vector(SRAM_ADDR_SIZE downto 1);
|
signal sram_output : std_logic_vector(15 downto 0);
|
signal sram_output : std_logic_vector(15 downto 0);
|
|
|
|
|
-- PROM table and interface signals --------------------------------------------
|
-- PROM table and interface signals --------------------------------------------
|
|
|
-- We'll simulate a 16-bit-wide static PROM (e.g. a Flash) with some serious
|
-- We'll simulate a 16-bit-wide static PROM (e.g. a Flash) with some serious
|
-- cycle time (70 or 90 ns).
|
-- cycle time (70 or 90 ns).
|
|
|
signal prom_rd_addr : t_prom_address;
|
signal prom_rd_addr : t_prom_address;
|
signal prom_output : std_logic_vector(7 downto 0);
|
signal prom_output : std_logic_vector(7 downto 0);
|
signal prom_oe_n : std_logic;
|
signal prom_oe_n : std_logic;
|
|
|
signal prom : t_prom := ( PROM_DATA );
|
signal prom : t_prom := ( PROM_DATA );
|
|
|
|
|
|
|
-- I/O devices -----------------------------------------------------------------
|
-- I/O devices -----------------------------------------------------------------
|
|
|
signal data_uart : std_logic_vector(31 downto 0);
|
signal data_uart : std_logic_vector(31 downto 0);
|
signal data_uart_status : std_logic_vector(31 downto 0);
|
signal data_uart_status : std_logic_vector(31 downto 0);
|
signal uart_tx_rdy : std_logic := '1';
|
signal uart_tx_rdy : std_logic := '1';
|
signal uart_rx_rdy : std_logic := '1';
|
signal uart_rx_rdy : std_logic := '1';
|
|
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
|
|
signal clk : std_logic := '0';
|
signal clk : std_logic := '0';
|
signal reset : std_logic := '1';
|
signal reset : std_logic := '1';
|
signal interrupt : std_logic := '0';
|
signal interrupt : std_logic := '0';
|
signal done : std_logic := '0';
|
signal done : std_logic := '0';
|
|
|
-- interface to asynchronous 16-bit-wide external SRAM
|
-- interface to asynchronous 16-bit-wide external SRAM
|
signal mpu_sram_address : t_word;
|
signal mpu_sram_address : t_word;
|
signal mpu_sram_data_rd : std_logic_vector(15 downto 0);
|
signal mpu_sram_data_rd : std_logic_vector(15 downto 0);
|
signal mpu_sram_data_wr : std_logic_vector(15 downto 0);
|
signal mpu_sram_data_wr : std_logic_vector(15 downto 0);
|
signal mpu_sram_byte_we_n : std_logic_vector(1 downto 0);
|
signal mpu_sram_byte_we_n : std_logic_vector(1 downto 0);
|
signal mpu_sram_oe_n : std_logic;
|
signal mpu_sram_oe_n : std_logic;
|
|
|
-- interface to i/o
|
-- interface to i/o
|
signal io_rd_data : std_logic_vector(31 downto 0);
|
signal io_rd_data : std_logic_vector(31 downto 0);
|
signal io_wr_data : std_logic_vector(31 downto 0);
|
signal io_wr_data : std_logic_vector(31 downto 0);
|
signal io_rd_addr : std_logic_vector(31 downto 2);
|
signal io_rd_addr : std_logic_vector(31 downto 2);
|
signal io_wr_addr : std_logic_vector(31 downto 2);
|
signal io_wr_addr : std_logic_vector(31 downto 2);
|
signal io_rd_vma : std_logic;
|
signal io_rd_vma : std_logic;
|
signal io_byte_we : std_logic_vector(3 downto 0);
|
signal io_byte_we : std_logic_vector(3 downto 0);
|
|
|
signal rxd : std_logic;
|
signal rxd : std_logic;
|
signal txd : std_logic;
|
signal txd : std_logic;
|
|
|
-- Other CPU signals
|
-- Other CPU signals
|
signal cpu_irq : std_logic_vector(7 downto 0);
|
signal cpu_irq : std_logic_vector(7 downto 0);
|
|
|
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
-- Logging signals
|
-- Logging signals
|
|
|
|
|
-- Log file
|
-- Log file
|
file log_file: TEXT open write_mode is "hw_sim_log.txt";
|
file log_file: TEXT open write_mode is "hw_sim_log.txt";
|
|
|
-- Console output log file
|
-- Console output log file
|
file con_file: TEXT open write_mode is "hw_sim_console_log.txt";
|
file con_file: TEXT open write_mode is "hw_sim_console_log.txt";
|
|
|
-- All the info needed by the logger is here
|
-- All the info needed by the logger is here
|
signal log_info : t_log_info;
|
signal log_info : t_log_info;
|
|
|
-- IRQ trigger simulation ------------------------------------------------------
|
-- IRQ trigger simulation ------------------------------------------------------
|
|
|
signal irq_trigger_addr : std_logic_vector(2 downto 0);
|
signal irq_trigger_addr : std_logic_vector(2 downto 0);
|
signal irq_trigger_data : std_logic_vector(31 downto 0);
|
signal irq_trigger_data : std_logic_vector(31 downto 0);
|
signal irq_trigger_load : std_logic;
|
signal irq_trigger_load : std_logic;
|
|
|
subtype t_irq_countdown is std_logic_vector(31 downto 0);
|
subtype t_irq_countdown is std_logic_vector(31 downto 0);
|
type t_irq_countdown_array is array(0 to 7) of t_irq_countdown;
|
type t_irq_countdown_array is array(0 to 7) of t_irq_countdown;
|
|
|
signal irq_countdown : t_irq_countdown_array;
|
signal irq_countdown : t_irq_countdown_array;
|
|
|
|
|
begin
|
begin
|
|
|
-- UUT instantiation -------------------------------------------------------
|
-- UUT instantiation -------------------------------------------------------
|
mpu: entity work.mips_mpu
|
mpu: entity work.mips_mpu
|
generic map (
|
generic map (
|
CLOCK_FREQ => 50000000,
|
CLOCK_FREQ => 50000000,
|
SRAM_ADDR_SIZE => 32
|
SRAM_ADDR_SIZE => 32
|
)
|
)
|
port map (
|
port map (
|
interrupt => cpu_irq(0),
|
interrupt => cpu_irq,
|
|
|
-- interface to FPGA i/o devices
|
-- interface to FPGA i/o devices
|
io_rd_data => io_rd_data,
|
io_rd_data => io_rd_data,
|
io_rd_addr => io_rd_addr,
|
io_rd_addr => io_rd_addr,
|
io_wr_addr => io_wr_addr,
|
io_wr_addr => io_wr_addr,
|
io_wr_data => io_wr_data,
|
io_wr_data => io_wr_data,
|
io_rd_vma => io_rd_vma,
|
io_rd_vma => io_rd_vma,
|
io_byte_we => io_byte_we,
|
io_byte_we => io_byte_we,
|
|
|
-- interface to asynchronous 16-bit-wide EXTERNAL SRAM
|
-- interface to asynchronous 16-bit-wide EXTERNAL SRAM
|
sram_address => mpu_sram_address,
|
sram_address => mpu_sram_address,
|
sram_data_rd => mpu_sram_data_rd,
|
sram_data_rd => mpu_sram_data_rd,
|
sram_data_wr => mpu_sram_data_wr,
|
sram_data_wr => mpu_sram_data_wr,
|
sram_byte_we_n => mpu_sram_byte_we_n,
|
sram_byte_we_n => mpu_sram_byte_we_n,
|
sram_oe_n => mpu_sram_oe_n,
|
sram_oe_n => mpu_sram_oe_n,
|
|
|
uart_rxd => rxd,
|
uart_rxd => rxd,
|
uart_txd => txd,
|
uart_txd => txd,
|
|
|
debug_info => OPEN,
|
debug_info => OPEN,
|
|
|
clk => clk,
|
clk => clk,
|
reset => reset
|
reset => reset
|
);
|
);
|
|
|
|
|
-- Master clock: free running clock used as main module clock --------------
|
-- Master clock: free running clock used as main module clock --------------
|
run_master_clock:
|
run_master_clock:
|
process(done, clk)
|
process(done, clk)
|
begin
|
begin
|
if done = '0' then
|
if done = '0' then
|
clk <= not clk after T/2;
|
clk <= not clk after T/2;
|
end if;
|
end if;
|
end process run_master_clock;
|
end process run_master_clock;
|
|
|
-- Main simulation process: reset MCU and wait for fixed period ------------
|
-- Main simulation process: reset MCU and wait for fixed period ------------
|
drive_uut:
|
drive_uut:
|
process
|
process
|
variable l : line;
|
variable l : line;
|
begin
|
begin
|
wait for T*4;
|
wait for T*4;
|
reset <= '0';
|
reset <= '0';
|
|
|
wait for T*SIMULATION_LENGTH;
|
wait for T*SIMULATION_LENGTH;
|
|
|
-- Flush console output to log console file (in case the end of the
|
-- Flush console output to log console file (in case the end of the
|
-- simulation caugh an unterminated line in the buffer)
|
-- simulation caugh an unterminated line in the buffer)
|
if log_info.con_line_ix > 1 then
|
if log_info.con_line_ix > 1 then
|
write(l, log_info.con_line_buf(1 to log_info.con_line_ix));
|
write(l, log_info.con_line_buf(1 to log_info.con_line_ix));
|
writeline(con_file, l);
|
writeline(con_file, l);
|
end if;
|
end if;
|
|
|
print("TB finished");
|
print("TB finished");
|
done <= '1';
|
done <= '1';
|
wait;
|
wait;
|
|
|
end process drive_uut;
|
end process drive_uut;
|
|
|
|
|
|
|
-- SRAM/FLASH mux (on a real board this would be a simple address decoder)
|
-- SRAM/FLASH mux (on a real board this would be a simple address decoder)
|
mpu_sram_data_rd <=
|
mpu_sram_data_rd <=
|
X"00" & prom_output when mpu_sram_address(31 downto 27)="10110" else
|
X"00" & prom_output when mpu_sram_address(31 downto 27)="10110" else
|
sram_output;
|
sram_output;
|
|
|
|
|
-- Do a very basic simulation of an external SRAM --------------------------
|
-- Do a very basic simulation of an external SRAM --------------------------
|
|
|
sram_chip_addr <= mpu_sram_address(SRAM_ADDR_SIZE downto 1);
|
sram_chip_addr <= mpu_sram_address(SRAM_ADDR_SIZE downto 1);
|
|
|
-- FIXME should add some verification of /WE
|
-- FIXME should add some verification of /WE
|
sram_output <=
|
sram_output <=
|
sram1(conv_integer(unsigned(sram_chip_addr))) &
|
sram1(conv_integer(unsigned(sram_chip_addr))) &
|
sram0(conv_integer(unsigned(sram_chip_addr))) when mpu_sram_oe_n='0'
|
sram0(conv_integer(unsigned(sram_chip_addr))) when mpu_sram_oe_n='0'
|
else (others => 'Z');
|
else (others => 'Z');
|
|
|
simulated_sram_write:
|
simulated_sram_write:
|
process(mpu_sram_byte_we_n, mpu_sram_address, mpu_sram_oe_n)
|
process(mpu_sram_byte_we_n, mpu_sram_address, mpu_sram_oe_n)
|
begin
|
begin
|
-- Write cycle
|
-- Write cycle
|
-- FIXME should add OE\ to write control logic
|
-- FIXME should add OE\ to write control logic
|
if mpu_sram_byte_we_n'event or mpu_sram_address'event then
|
if mpu_sram_byte_we_n'event or mpu_sram_address'event then
|
if mpu_sram_byte_we_n(1)='0' then
|
if mpu_sram_byte_we_n(1)='0' then
|
sram1(conv_integer(unsigned(sram_chip_addr))) <= mpu_sram_data_wr(15 downto 8);
|
sram1(conv_integer(unsigned(sram_chip_addr))) <= mpu_sram_data_wr(15 downto 8);
|
end if;
|
end if;
|
if mpu_sram_byte_we_n(0)='0' then
|
if mpu_sram_byte_we_n(0)='0' then
|
sram0(conv_integer(unsigned(sram_chip_addr))) <= mpu_sram_data_wr( 7 downto 0);
|
sram0(conv_integer(unsigned(sram_chip_addr))) <= mpu_sram_data_wr( 7 downto 0);
|
end if;
|
end if;
|
end if;
|
end if;
|
end process simulated_sram_write;
|
end process simulated_sram_write;
|
|
|
|
|
-- Do a very basic simulation of an external PROM (FLASH) ------------------
|
-- Do a very basic simulation of an external PROM (FLASH) ------------------
|
-- (wired to the same bus as the sram and both are static).
|
-- (wired to the same bus as the sram and both are static).
|
|
|
prom_rd_addr <= mpu_sram_address(PROM_ADDR_SIZE+1 downto 2);
|
prom_rd_addr <= mpu_sram_address(PROM_ADDR_SIZE+1 downto 2);
|
|
|
prom_oe_n <= mpu_sram_oe_n;
|
prom_oe_n <= mpu_sram_oe_n;
|
|
|
prom_output <=
|
prom_output <=
|
prom(conv_integer(unsigned(prom_rd_addr)))(31 downto 24) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="00" else
|
prom(conv_integer(unsigned(prom_rd_addr)))(31 downto 24) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="00" else
|
prom(conv_integer(unsigned(prom_rd_addr)))(23 downto 16) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="01" else
|
prom(conv_integer(unsigned(prom_rd_addr)))(23 downto 16) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="01" else
|
prom(conv_integer(unsigned(prom_rd_addr)))(15 downto 8) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="10" else
|
prom(conv_integer(unsigned(prom_rd_addr)))(15 downto 8) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="10" else
|
prom(conv_integer(unsigned(prom_rd_addr)))( 7 downto 0) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="11" else
|
prom(conv_integer(unsigned(prom_rd_addr)))( 7 downto 0) when prom_oe_n='0' and mpu_sram_address(1 downto 0)="11" else
|
(others => 'Z');
|
(others => 'Z');
|
|
|
|
|
-- Simulate dummy I/O traffic external to the MCU --------------------------
|
-- Simulate dummy I/O traffic external to the MCU --------------------------
|
-- the only IO present is the test interrupt trigger registers
|
-- the only IO present is the test interrupt trigger registers
|
simulated_io:
|
simulated_io:
|
process(clk)
|
process(clk)
|
variable i : integer;
|
variable i : integer;
|
variable uart_data : integer;
|
variable uart_data : integer;
|
begin
|
begin
|
if clk'event and clk='1' then
|
if clk'event and clk='1' then
|
if io_byte_we /= "0000" then
|
if io_byte_we /= "0000" then
|
if io_wr_addr(31 downto 16)=X"2001" then
|
if io_wr_addr(31 downto 16)=X"2001" then
|
irq_trigger_load <= '1';
|
irq_trigger_load <= '1';
|
irq_trigger_data <= io_wr_data;
|
irq_trigger_data <= io_wr_data;
|
irq_trigger_addr <= io_wr_addr(4 downto 2);
|
irq_trigger_addr <= io_wr_addr(4 downto 2);
|
else
|
else
|
irq_trigger_load <= '0';
|
irq_trigger_load <= '0';
|
end if;
|
end if;
|
else
|
else
|
irq_trigger_load <= '0';
|
irq_trigger_load <= '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
end process simulated_io;
|
end process simulated_io;
|
|
|
-- Simulate IRQs -----------------------------------------------------------
|
-- Simulate IRQs -----------------------------------------------------------
|
irq_trigger_registers:
|
irq_trigger_registers:
|
process(clk)
|
process(clk)
|
variable index : integer range 0 to 7;
|
variable index : integer range 0 to 7;
|
begin
|
begin
|
if clk'event and clk='1' then
|
if clk'event and clk='1' then
|
if reset='1' then
|
if reset='1' then
|
cpu_irq <= "00000000";
|
cpu_irq <= "00000000";
|
else
|
else
|
if irq_trigger_load='1' then
|
if irq_trigger_load='1' then
|
index := conv_integer(irq_trigger_addr);
|
index := conv_integer(irq_trigger_addr);
|
irq_countdown(index) <= irq_trigger_data;
|
irq_countdown(index) <= irq_trigger_data;
|
else
|
else
|
for index in 0 to 7 loop
|
for index in 0 to 7 loop
|
if irq_countdown(index) = X"00000001" then
|
if irq_countdown(index) = X"00000001" then
|
cpu_irq(index) <= '1';
|
cpu_irq(index) <= '1';
|
irq_countdown(index) <= irq_countdown(index) - 1;
|
irq_countdown(index) <= irq_countdown(index) - 1;
|
elsif irq_countdown(index)/=X"00000000" then
|
elsif irq_countdown(index)/=X"00000000" then
|
irq_countdown(index) <= irq_countdown(index) - 1;
|
irq_countdown(index) <= irq_countdown(index) - 1;
|
cpu_irq(index) <= '0';
|
cpu_irq(index) <= '0';
|
else
|
else
|
cpu_irq(index) <= '0';
|
cpu_irq(index) <= '0';
|
end if;
|
end if;
|
end loop;
|
end loop;
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process irq_trigger_registers;
|
end process irq_trigger_registers;
|
|
|
|
|
-- This is useless (the simulated UART will not be actually used)
|
-- This is useless (the simulated UART will not be actually used)
|
-- but at least prevents the simulator from optimizing the logic away.
|
-- but at least prevents the simulator from optimizing the logic away.
|
rxd <= txd;
|
rxd <= txd;
|
|
|
|
|
-- Logging process: launch logger function ---------------------------------
|
-- Logging process: launch logger function ---------------------------------
|
log_execution:
|
log_execution:
|
process
|
process
|
begin
|
begin
|
log_cpu_activity(clk, reset, done,
|
log_cpu_activity(clk, reset, done,
|
"mips_tb/mpu", "cpu",
|
"mips_tb/mpu", "cpu",
|
log_info, "log_info",
|
log_info, "log_info",
|
LOG_TRIGGER_ADDRESS, log_file, con_file);
|
LOG_TRIGGER_ADDRESS, log_file, con_file);
|
wait;
|
wait;
|
end process log_execution;
|
end process log_execution;
|
|
|
end architecture testbench;
|
end architecture testbench;
|
|
|