From 91c350feb9cce338bdef00d0b701e1acd533f424 Mon Sep 17 00:00:00 2001 From: rs <> Date: Wed, 2 Jul 2025 23:57:27 -0500 Subject: [PATCH] Update project * Add single stepping back to CPU * Separate flash and RAM cyc signals * Add timers * Fix interrupt bug --- libraries/timer/tests/test_timer.vhd | 90 +++++++++++++++++ libraries/timer/timer.vhd | 127 ++++++++++++++++++++++++ projects/cpu_0/cpu.vhd | 39 ++++++-- projects/cpu_0/nexys2.vhd | 140 +++++++++++++++++++++------ projects/cpu_0/tests/test_cpu.vhd | 8 +- 5 files changed, 368 insertions(+), 36 deletions(-) create mode 100644 libraries/timer/tests/test_timer.vhd create mode 100644 libraries/timer/timer.vhd diff --git a/libraries/timer/tests/test_timer.vhd b/libraries/timer/tests/test_timer.vhd new file mode 100644 index 0000000..c678538 --- /dev/null +++ b/libraries/timer/tests/test_timer.vhd @@ -0,0 +1,90 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_timer is +end test_timer; + + +architecture behavior of test_timer is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + signal cyc_i: std_logic; + signal stb_i: std_logic; + signal we_i: std_logic; + signal ack_o: std_logic; + signal adr_i: std_logic_vector(1 downto 0); + signal dat_i: std_logic_vector(7 downto 0); + signal dat_o: std_logic_vector(7 downto 0); + signal zero: std_logic; + +begin + + p_test: process + begin + -- Initial values + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + dat_i <= (others => '0'); + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait for CLK_I_PERIOD; + + -- Set a value in the timer and start it + dat_i <= x"00"; + adr_i <= "11"; + we_i <= '1'; + stb_i <= '1'; + cyc_i <= '1'; + wait for CLK_I_PERIOD; + dat_i <= x"00"; + adr_i <= "10"; + wait for CLK_I_PERIOD; + dat_i <= x"01"; + adr_i <= "01"; + wait for CLK_I_PERIOD; + dat_i <= "00001111"; + adr_i <= "00"; + wait for CLK_I_PERIOD; + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + + -- Done + wait; + end process; + + + e_uut: entity work.timer + port map ( + rst_i => rst_i, + clk_i => clk_i, + cyc_i => cyc_i, + stb_i => stb_i, + we_i => we_i, + ack_o => ack_o, + adr_i => adr_i, + dat_i => dat_i, + dat_o => dat_o, + zero => zero + ); + + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/timer/timer.vhd b/libraries/timer/timer.vhd new file mode 100644 index 0000000..02ae009 --- /dev/null +++ b/libraries/timer/timer.vhd @@ -0,0 +1,127 @@ +-------------------------------------------------------------------------------- +-- timer - Wishbone-bus generic timer +-------------------------------------------------------------------------------- +-- +-- +---+---+---+---+---+---+---+---+ +-- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-- +---+---+---+---+---+---+---+---+ +-- 0 | | Z |IEN|AUT|ENA| CTRL +-- +---------------+---+---+---+---+ +-- 1 | COUNT_L | (TRIGGER) +-- +-------------------------------+ +-- 2 | COUNT_M | +-- +-------------------------------+ +-- 3 | COUNT_H | +-- +-------------------------------+ +-- +-- CTRL register bits +-- ENA - Enable counter +-- AUT - Enable auto-restart +-- IEN - Interupt enable +-- Z - Counter hit zero interrupt (W1C) +-- +-- COUNT_L/M/H +-- 24-bit counter value access +-- Counter value is loaded into registers when COUNT_L is read +-- Counter value and auto-reload value are captured when COUNT_L is written +-- +-- Notes: +-- Counter counts at 1/256th of the clock rate +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + + +entity timer is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(1 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + zero: out std_logic + ); +end timer; + + +architecture behavioral of timer is + + signal ena_reg: std_logic; + signal aut_reg: std_logic; + signal ien_reg: std_logic; + signal z_reg: std_logic; + signal count_reg: std_logic_vector(31 downto 0); + signal reload_reg: std_logic_vector(23 downto 0); + signal temp_m_reg: std_logic_vector(7 downto 0); + signal temp_h_reg: std_logic_vector(7 downto 0); + + signal is_zero: std_logic; + +begin + + process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i, + temp_h_reg, temp_m_reg, z_reg, count_reg, reload_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + ena_reg <= '0'; + aut_reg <= '0'; + ien_reg <= '0'; + z_reg <= '0'; + count_reg <= (others => '0'); + reload_reg <= (others => '0'); + temp_m_reg <= (others => '0'); + temp_h_reg <= (others => '0'); + else + if ena_reg = '1' and is_zero = '0' then + count_reg <= std_logic_vector(unsigned(count_reg) - 1); + elsif is_zero = '1' then + z_reg <= '1'; + if aut_reg = '1' then + count_reg <= reload_reg & x"ff"; + end if; + end if; + + if cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when "00" => ena_reg <= dat_i(0); + aut_reg <= dat_i(1); + ien_reg <= dat_i(2); + z_reg <= z_reg and (not dat_i(3)); -- W1C + when "01" => count_reg <= temp_h_reg & temp_m_reg & dat_i & x"ff"; + reload_reg <= temp_h_reg & temp_m_reg & dat_i; + when "10" => temp_m_reg <= dat_i; + when "11" => temp_h_reg <= dat_i; + when others => null; + end case; + elsif cyc_i = '1' and stb_i = '1' and we_i = '0' and adr_i = "01" then + -- Load temp registers for reading in later cycles + temp_m_reg <= count_reg(23 downto 16); + temp_h_reg <= count_reg(31 downto 24); + end if; + end if; + end if; + end process; + + with adr_i select dat_o <= + "0000" & z_reg & ien_reg & aut_reg & ena_reg when "00", + count_reg(15 downto 8) when "01", + temp_m_reg when "10", + temp_h_reg when others; + + ack_o <= '1'; + + is_zero <= not or_reduce(count_reg(31 downto 8)); + + zero <= z_reg and ien_reg; + +end behavioral; diff --git a/projects/cpu_0/cpu.vhd b/projects/cpu_0/cpu.vhd index d7dde24..e8fd785 100644 --- a/projects/cpu_0/cpu.vhd +++ b/projects/cpu_0/cpu.vhd @@ -52,7 +52,16 @@ entity cpu is dat_i: in std_logic_vector(7 downto 0); int_i: in std_logic; - vec_i: in std_logic_vector(7 downto 0) + vec_i: in std_logic_vector(7 downto 0); + + -- Debugging + halt_i: in std_logic; + step_i: in std_logic; + pc_o: out std_logic_vector(31 downto 0); + ins_o: out std_logic_vector(7 downto 0); + t_o: out std_logic_vector(31 downto 0); + n_o: out std_logic_vector(31 downto 0); + r_o: out std_logic_vector(31 downto 0) ); end cpu; @@ -60,6 +69,7 @@ end cpu; architecture behavioral of cpu is type state_t is ( + S_WAIT, S_FETCH2, S_RESET, S_FETCH, S_MEMORY, S_IMM8, S_IMM32_0, S_IMM32_1, S_IMM32_2, S_IMM32_3, S_LD8, S_LD32_0, S_LD32_1, S_LD32_2, S_LD32_3, @@ -155,6 +165,13 @@ architecture behavioral of cpu is begin + -- Debugging signals + pc_o <= pc_reg; + ins_o <= ins_reg; + t_o <= s_t_reg; + n_o <= s_n_reg; + r_o <= r_tos_reg; + -- Control state machine process (rst_i, clk_i, state_next) begin @@ -167,7 +184,7 @@ begin end if; end process; - process (state_reg, ack_i, ins_decode, int_i, ins_pc_sel, imask_reg) + process (state_reg, ack_i, ins_decode, int_i, ins_pc_sel, imask_reg, step_i, halt_i) begin state_next <= state_reg; cyc_o <= '0'; @@ -195,6 +212,16 @@ begin case state_reg is when S_FETCH => + if halt_i = '0' then + state_next <= S_FETCH2; + elsif step_i = '0' then + state_next <= S_WAIT; + end if; + when S_WAIT => + if step_i = '1' then + state_next <= S_FETCH2; + end if; + when S_FETCH2 => adr_sel <= ADR_PC; cyc_o <= '1'; stb_o <= '1'; @@ -212,7 +239,7 @@ begin -- in the S_EXEC state, which would otherwise be necessary to make -- sure we don't enter an interrupt after a "mask-int" instruction -- and then return with interrupts unmasked into a critical section - imask_set <= ins_decode.imask_set; + imask_clr <= ins_decode.imask_clr; case ins_decode.mem_op is when MEM_IMM8 => state_next <= S_IMM8; @@ -414,10 +441,10 @@ begin -- The flag will be cleared in the next clock cycle, so the next -- instruction is guaranteed to be executed if an interrupt is -- pending, useful for clearing the interrupt flag and then returning - imask_clr <= ins_decode.imask_clr; + imask_set <= ins_decode.imask_set; -- Check for interrupts during the execute step so we don't have any in-progress memory operations to cancel - if int_i = '1' and imask_reg = '0' then + if int_i = '1' and imask_reg = '1' then state_next <= S_INT; else state_next <= S_FETCH; @@ -441,7 +468,7 @@ begin pc_sel <= PC_INTVEC; -- Mask further interruptions - imask_set <= '1'; + imask_clr <= '1'; state_next <= S_FETCH; diff --git a/projects/cpu_0/nexys2.vhd b/projects/cpu_0/nexys2.vhd index eb581be..4e44ee8 100644 --- a/projects/cpu_0/nexys2.vhd +++ b/projects/cpu_0/nexys2.vhd @@ -78,7 +78,8 @@ architecture behavioral of nexys2 is signal dat_mosi: std_logic_vector(7 downto 0); signal dat_miso: std_logic_vector(7 downto 0); - signal mem_cyc: std_logic; + signal fls_cyc: std_logic; + signal ram_cyc: std_logic; signal mem_ack: std_logic; signal mem_miso: std_logic_vector(7 downto 0); @@ -98,6 +99,14 @@ architecture behavioral of nexys2 is signal uart_ack: std_logic; signal uart_miso: std_logic_vector(7 downto 0); + signal timer0_cyc: std_logic; + signal timer0_ack: std_logic; + signal timer0_miso: std_logic_vector(7 downto 0); + + signal timer1_cyc: std_logic; + signal timer1_ack: std_logic; + signal timer1_miso: std_logic_vector(7 downto 0); + -- Interrupt signals signal host_flags: std_logic_vector(7 downto 0); @@ -121,6 +130,9 @@ architecture behavioral of nexys2 is signal uart_err_parity: std_logic; signal uart_err_overflow: std_logic; + signal timer0_zero: std_logic; + signal timer1_zero: std_logic; + -- Memory physical interface, routed through host controller signal d_MemOE: std_logic; signal d_MemWR: std_logic; @@ -143,6 +155,16 @@ architecture behavioral of nexys2 is signal int_vec: std_logic_vector(7 downto 0); signal ints: std_logic_vector(15 downto 0); + -- Debug signals + signal deb_wait: std_logic; + signal deb_pc: std_logic_vector(31 downto 0); + signal deb_ins: std_logic_vector(7 downto 0); + signal deb_t: std_logic_vector(31 downto 0); + signal deb_n: std_logic_vector(31 downto 0); + signal deb_r: std_logic_vector(31 downto 0); + signal debug_i: std_logic_vector(63 downto 0); + signal debug_o: std_logic_vector(63 downto 0); + begin e_cpu: entity work.cpu @@ -159,9 +181,23 @@ begin dat_i => dat_miso, int_i => int_cpu, - vec_i => int_vec + vec_i => int_vec, + + halt_i => sw(7), + step_i => deb_wait, + pc_o => deb_pc, + ins_o => deb_ins, + t_o => deb_t, + n_o => deb_n, + r_o => deb_r ); + deb_wait <= debug_i(0); + with debug_i(2 downto 1) select debug_o <= + x"000000" & deb_ins & deb_pc when "00", + deb_n & deb_t when "01", + x"00000000" & deb_r when others; + int_vec(7 downto 4) <= (others => '0'); ints(0) <= '0'; e_int: entity work.int_ctrl @@ -183,7 +219,7 @@ begin -- UART: 0x02004010-0x02004017 e_mapper: entity utility.wb_mapper_a32d8 generic map ( - N => 5 + N => 8 ) port map ( cyc_i => cyc, @@ -191,35 +227,50 @@ begin adr_i => adr, dat_o => dat_miso, - mask(0) => "00000010000000000000000000000000", - mask(1) => "00000010000000000100000000000000", - mask(2) => "00000010000000000100000000011000", + mask(0) => "00000011000000000000000000000000", + mask(1) => "00000011000000000000000000000000", + mask(2) => "00000010000000000100000000000000", mask(3) => "00000010000000000100000000011000", mask(4) => "00000010000000000100000000011000", + mask(5) => "00000010000000000100000000011000", + mask(6) => "00000010000000000100000000011100", + mask(7) => "00000010000000000100000000011100", match(0) => "00000000000000000000000000000000", - match(1) => "00000010000000000000000000000000", - match(2) => "00000010000000000100000000000000", - match(3) => "00000010000000000100000000001000", - match(4) => "00000010000000000100000000010000", - - cyc_o(0) => mem_cyc, - cyc_o(1) => tile_cyc, - cyc_o(2) => host_cyc, - cyc_o(3) => ps2_cyc, - cyc_o(4) => uart_cyc, + match(1) => "00000001000000000000000000000000", + match(2) => "00000010000000000000000000000000", + match(3) => "00000010000000000100000000000000", + match(4) => "00000010000000000100000000001000", + match(5) => "00000010000000000100000000010000", + match(6) => "00000010000000000100000000011000", + match(7) => "00000010000000000100000000011100", + + cyc_o(0) => fls_cyc, + cyc_o(1) => ram_cyc, + cyc_o(2) => tile_cyc, + cyc_o(3) => host_cyc, + cyc_o(4) => ps2_cyc, + cyc_o(5) => uart_cyc, + cyc_o(6) => timer0_cyc, + cyc_o(7) => timer1_cyc, ack_i(0) => mem_ack, - ack_i(1) => tile_ack, - ack_i(2) => host_ack, - ack_i(3) => ps2_ack, - ack_i(4) => uart_ack, + ack_i(1) => mem_ack, + ack_i(2) => tile_ack, + ack_i(3) => host_ack, + ack_i(4) => ps2_ack, + ack_i(5) => uart_ack, + ack_i(6) => timer0_ack, + ack_i(7) => timer1_ack, dat_i(0) => mem_miso, - dat_i(1) => tile_miso, - dat_i(2) => host_miso, - dat_i(3) => ps2_miso, - dat_i(4) => uart_miso + dat_i(1) => mem_miso, + dat_i(2) => tile_miso, + dat_i(3) => host_miso, + dat_i(4) => ps2_miso, + dat_i(5) => uart_miso, + dat_i(6) => timer0_miso, + dat_i(7) => timer1_miso ); @@ -229,11 +280,12 @@ begin clk_i => d_clk, -- Internal access - cyc_i => mem_cyc, + fls_cyc_i => fls_cyc, + ram_cyc_i => ram_cyc, stb_i => stb, we_i => we, ack_o => mem_ack, - adr_i => adr(24 downto 0), + adr_i => adr(23 downto 0), dat_i => dat_mosi, dat_o => mem_miso, @@ -291,8 +343,8 @@ begin -- Signals to the internal device d_rst_o => d_rst, d_flags_o => host_flags, - debug_i => (others => '0'), - debug_o => open, + debug_i => debug_o, + debug_o => debug_i, -- Internal access to control registers d_cyc_i => host_cyc, @@ -428,4 +480,36 @@ begin rx => RsRx ); + + ints(4) <= timer0_zero; + e_timer0: entity timer.timer + port map ( + rst_i => d_rst, + clk_i => d_clk, + cyc_i => timer0_cyc, + stb_i => stb, + we_i => we, + ack_o => timer0_ack, + adr_i => adr(1 downto 0), + dat_i => dat_mosi, + dat_o => timer0_miso, + zero => timer0_zero + ); + + + ints(5) <= timer1_zero; + e_timer1: entity timer.timer + port map ( + rst_i => d_rst, + clk_i => d_clk, + cyc_i => timer1_cyc, + stb_i => stb, + we_i => we, + ack_o => timer1_ack, + adr_i => adr(1 downto 0), + dat_i => dat_mosi, + dat_o => timer1_miso, + zero => timer1_zero + ); + end behavioral; diff --git a/projects/cpu_0/tests/test_cpu.vhd b/projects/cpu_0/tests/test_cpu.vhd index 183cc3f..a8fc2c7 100644 --- a/projects/cpu_0/tests/test_cpu.vhd +++ b/projects/cpu_0/tests/test_cpu.vhd @@ -65,8 +65,12 @@ begin -- INIT => x"0a100000000c05000000000000000000015a0b00000000000000000000000000" -- Test conditional branch and interrupts - -- 0x00: jz 0x10 #0xa5 ; 0x10: jmp 0x10 - INIT => x"0d1000000001a50b00000000000000000c100000000000000000000000000000" + -- 0x00: jz 0x10 #0xa5 ien ; 0x10: ien jmp 0x11 + -- INIT => x"0d1000000001a5190b00000000000000190c1100000000000000000000000000" + + -- Test jumptable + -- 0x00: #0x03 dup + dup + #0x00000010 + @32 >r ; + INIT => x"01031610161002100000001004080b0011111111222222223333333344444444" ) port map ( rst_i => rst_i, -- 2.43.0