URL
https://opencores.org/ocsvn/forth-cpu/forth-cpu/trunk
Subversion Repositories forth-cpu
[/] [forth-cpu/] [trunk/] [tb.vhd] - Rev 5
Compare with Previous | Blame | View Log
------------------------------------------------------------------------------- --| @file tb.vhd --| @brief Main test bench. --| --| @author Richard James Howe. --| @copyright Copyright 2013-2019 Richard James Howe. --| @license MIT --| @email howe.r.j.89@gmail.com --| --| This test bench does quite a lot. It is not like normal VHDL test benches --| in the fact that it uses configurable variables that it reads in from a --| file, which it does in an awkward but usable fashion. It also has a --| partially working way of connecting a simulated UART to STDIN/STDOUT, which --| is a work in progress. --| --| It also tests multiple modules. --| ------------------------------------------------------------------------------- library ieee,work; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; use std.textio.all; use work.util.all; use work.core_pkg.all; use work.vga_pkg.all; entity tb is end tb; architecture testing of tb is constant g: common_generics := ( clock_frequency => 100_000_000, asynchronous_reset => true, delay => 0 ns); constant number_of_interrupts: positive := 8; constant uart_baud: positive := 115200; constant configuration_file_name: string := "tb.cfg"; constant uart_tx_time: time := (10*1000 ms) / 115200; constant uart_default_input: std_ulogic_vector(7 downto 0) := x"AA"; constant reset_period_us: natural := 1; constant jitter_on: boolean := false; constant clock_period: time := 1000 ms / g.clock_frequency; constant tb_vga_on: boolean := false; constant tb_uart_on: boolean := false; constant tb_util_on: boolean := false; -- Test bench configurable options -- type configurable_items is record number_of_iterations: natural; -- 0 == loop forever verbose: boolean; report_number: natural; interactive: boolean; input_wait_for: time; end record; function set_configuration_items(ci: configuration_items) return configurable_items is variable r: configurable_items; begin r.number_of_iterations := ci(0).value; r.verbose := ci(1).value > 0; r.interactive := ci(2).value > 0; r.input_wait_for := ci(3).value * 1 ms; r.report_number := ci(4).value; return r; end function; constant configuration_default: configuration_items(0 to 4) := ( (name => "Cycles ", value => 1000), (name => "Verbose ", value => 1), (name => "Interact", value => 0), (name => "InWaitMs", value => 8), (name => "LogFor ", value => 256)); -- Test bench configurable options -- signal stop: boolean := false; signal dbgi: cpu_debug_interface; signal clk: std_ulogic := '0'; signal rst: std_ulogic := '0'; -- Basic I/O signal btnu: std_ulogic := '0'; -- button up signal btnd: std_ulogic := '0'; -- button down signal btnc: std_ulogic := '0'; -- button centre signal btnl: std_ulogic := '0'; -- button left signal btnr: std_ulogic := '0'; -- button right signal sw: std_ulogic_vector(7 downto 0) := (others => '0'); -- switches signal an: std_ulogic_vector(3 downto 0) := (others => '0'); -- anodes 8 segment display signal ka: std_ulogic_vector(7 downto 0) := (others => '0'); -- kathodes 8 segment display signal ld: std_ulogic_vector(7 downto 0) := (others => '0'); -- leds -- VGA signal o_vga: vga_physical_interface; signal hsync_gone_high: boolean := false; signal vsync_gone_high: boolean := false; -- HID signal ps2_keyboard_data: std_ulogic := '0'; signal ps2_keyboard_clk: std_ulogic := '0'; -- UART signal rx: std_ulogic := '0'; signal tx: std_ulogic := '0'; signal dout_ack, dout_stb: std_ulogic := '0'; signal din_ack, din_stb: std_ulogic := '0'; signal dout: std_ulogic_vector(7 downto 0) := (others => '0'); signal din: std_ulogic_vector(7 downto 0) := (others => '0'); -- Wave form generator signal gen_dout: std_ulogic_vector(15 downto 0) := (others => '0'); shared variable cfg: configurable_items := set_configuration_items(configuration_default); signal configured: boolean := false; signal ram_cs: std_ulogic := 'X'; signal mem_oe: std_ulogic := 'X'; -- negative logic signal mem_wr: std_ulogic := 'X'; -- negative logic signal mem_adv: std_ulogic := 'X'; -- negative logic signal mem_wait: std_ulogic := 'X'; -- positive! signal flash_cs: std_ulogic := 'X'; signal flash_rp: std_ulogic := 'X'; signal mem_addr: std_ulogic_vector(26 downto 1) := (others => 'X'); signal mem_data: std_logic_vector(15 downto 0) := (others => 'X'); begin ---- Units under test ---------------------------------------------------------- mem_data <= (others => '0') when mem_oe = '1' else (others => 'Z'); uut: entity work.top generic map( g => g, reset_period_us => reset_period_us, uart_baud => uart_baud) port map( debug => dbgi, clk => clk, -- rst => rst, btnu => btnu, btnd => btnd, btnc => btnc, btnl => btnl, btnr => btnr, sw => sw, an => an, ka => ka, ld => ld, rx => rx, tx => tx, o_vga => o_vga, ps2_keyboard_data => ps2_keyboard_data, ps2_keyboard_clk => ps2_keyboard_clk, ram_cs => ram_cs, mem_oe => mem_oe, mem_wr => mem_wr, mem_adv => mem_adv, mem_wait => mem_wait, flash_cs => flash_cs, flash_rp => flash_rp, mem_addr => mem_addr, mem_data => mem_data); -- NB. It would be nice to configure these as off/on, as well as -- controlling how long they run for from here. util_g: if tb_util_on generate uut_util: work.util.util_tb generic map(g => g); end generate; vga_g: if tb_vga_on generate uut_vga: work.vga_pkg.vt100_tb generic map(g => g); end generate; uart_g: if tb_uart_on generate uut_uart: work.uart_pkg.uart_tb generic map(g => g); end generate; uart_0_blk: block signal uart_clock_rx_we, uart_clock_tx_we, uart_control_we: std_ulogic := '0'; signal uart_reg: std_ulogic_vector(15 downto 0); begin uart_0: work.uart_pkg.uart_core generic map (g => g, baud => uart_baud) port map ( clk => clk, rst => rst, tx_di => din, tx_we => din_stb, tx_ok => din_ack, tx => rx, rx => tx, rx_ok => open, rx_nd => dout_stb, rx_do => dout, rx_re => dout_ack, reg => uart_reg, clock_reg_tx_we => uart_clock_tx_we, clock_reg_rx_we => uart_clock_rx_we, control_reg_we => uart_control_we); end block; ------ Simulation only processes ---------------------------------------------- clk_process: process variable seed1, seed2 : positive; variable r : real; variable jit_high, jit_low: time := 0 ns; begin while not stop loop if jitter_on then uniform(seed1, seed2, r); jit_high := r * g.delay; uniform(seed1, seed2, r); jit_low := r * g.delay; uniform(seed1, seed2, r); if r < 0.5 then jit_high := -jit_high; end if; uniform(seed1, seed2, r); if r < 0.5 then jit_low := -jit_low; end if; else jit_high := 0 ns; jit_low := 0 ns; end if; clk <= '1'; wait for (clock_period / 2) + jit_high; clk <= '0'; wait for (clock_period / 2) + jit_low; end loop; report "clk_process end"; wait; end process; output_process: process variable oline: line; variable c: character; variable have_char: boolean := true; begin wait until configured; if not cfg.interactive then report "Output turned off"; report "output_process end"; wait; end if; report "Writing to STDOUT"; while not stop loop wait until (dout_stb = '1' or stop); if not stop then c := character'val(to_integer(unsigned(dout))); write(oline, c); have_char := true; if dout = x"0d" then writeline(output, oline); have_char := false; end if; wait for clock_period; dout_ack <= '1'; wait for clock_period; dout_ack <= '0'; end if; end loop; if have_char then writeline(output, oline); end if; report "output_process end"; wait; end process; -- The Input and Output mechanism that allows the tester to -- interact with the running simulation needs more work, it is buggy -- and experimental, but demonstrates the principle - that a VHDL -- test bench can be interacted with at run time. input_process: process variable c: character := ' '; variable iline: line; -- variable oline: line; variable good: boolean := true; variable eoi: boolean := false; begin din_stb <= '0'; din <= x"00"; wait until configured; if not cfg.interactive then din_stb <= '1'; din <= uart_default_input; report "input process non-interactive"; report "input_process end"; wait; end if; report "Waiting for " & time'image(cfg.input_wait_for) & " (before reading from STDIN)"; wait for cfg.input_wait_for; report "Reading from STDIN (Hit EOF/CTRL-D/CTRL-Z After entering a line)"; while (not endfile(input)) and not stop and eoi = false loop report "readline..."; readline(input, iline); good := true; while good and not stop loop read(iline, c, good); if good then report "" & c; else report "EOL/EOI"; c := LF; eoi := true; end if; din <= std_ulogic_vector(to_unsigned(character'pos(c), din'length)); din_stb <= '1'; wait for clock_period; din_stb <= '0'; wait for 100 us; end loop; end loop; report "input_process end"; wait; end process; hsync_gone_high <= true when o_vga.hsync = '1' else hsync_gone_high; vsync_gone_high <= true when o_vga.vsync = '1' else vsync_gone_high; -- I/O settings go here. stimulus_process: process variable w: line; variable count: integer := 0; function stringify(slv: std_ulogic_vector) return string is begin return integer'image(to_integer(unsigned(slv))); end stringify; procedure element(l: inout line; we: boolean; name: string; slv: std_ulogic_vector) is begin if we then write(l, name & "(" & stringify(slv) & ") "); end if; end procedure; procedure element(l: inout line; name: string; slv: std_ulogic_vector) is begin element(l, true, name, slv); end procedure; function reportln(debug: cpu_debug_interface; cycles: integer) return line is variable l: line; begin write(l, integer'image(cycles) & ": "); element(l, "pc", debug.pc); element(l, "insn", debug.insn); element(l, "daddr", debug.daddr); element(l, "dout", debug.dout); return l; end function; variable configuration_values: configuration_items(configuration_default'range) := configuration_default; begin -- write_configuration_tb(configuration_file_name, configuration_default); read_configuration_tb(configuration_file_name, configuration_values); cfg := set_configuration_items(configuration_values); configured <= true; rst <= '1'; wait for clock_period * 2; rst <= '0'; if cfg.number_of_iterations = 0 then report "RUNNING FOREVER: number of iterations is zero" severity warning; report "stimulus_process end"; wait; end if; for i in 0 to cfg.number_of_iterations loop if cfg.verbose then if count < cfg.report_number then w := reportln(dbgi, count); writeline(OUTPUT, w); count := count + 1; elsif count < cfg.report_number + 1 then report "Simulation continuing: Reporting turned off"; count := count + 1; end if; end if; wait for clock_period * 1; end loop; -- These HSYNC and VSYNC asserts are included under the assumption -- that the image running on the H2 CPU will initiate the VGA, if -- it does not (perhaps because it is running it's own initialization -- routines), then the HSYNC or VSYNC will never go high - so this is -- not necessarily an error. -- -- It would be nice to test the other peripherals as -- well, the CPU-ID should be written to the LED 7 Segment -- displays, however we only get the cathode and anode -- values out of the unit. assert hsync_gone_high report "HSYNC not active - H2 failed to initialize VGA module"; assert vsync_gone_high report "VSYNC not active - H2 failed to initialize VGA module"; stop <= true; report "stimulus_process end"; wait; end process; end architecture; ------ END ---------------------------------------------------------------------