Core can now boot Zephyr RTOS

Added Interrupt handling support:
- Int enable masks
- external interrupt
- interrupt enable CSR
- illegal instruction
- system call instruction
- breakpoints
- interrupt CSR manipulation
- correct nextPC resume/branch target selection
Added debug data for CPU trace support
Added vexrisc IRQ csrs for testing with 3rd party sw
Added LINT unit locally arbitrates IRQs into priorities
FIX: correctly sign extend data from memory controller
FIX: set ALU to not branch on CSR unit ops
FIX: correctly detect invalid operations in decode stage
FIX: set signals not outputs in decode
Change to use two regs arrays in register set to infer two port rams
This commit is contained in:
Colin Riley 2020-05-17 23:39:17 +01:00
parent 01ac31c43d
commit 8803d1392d
9 changed files with 385 additions and 141 deletions

View file

@ -3,7 +3,7 @@
-- Description: control unit
--
----------------------------------------------------------------------------------
-- Copyright 2016 Colin Riley
-- Copyright 2016,2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -27,6 +27,7 @@ entity control_unit is
Port (
I_clk : in STD_LOGIC;
I_reset : in STD_LOGIC;
I_halt: in STD_LOGIC;
I_aluop : in STD_LOGIC_VECTOR (6 downto 0);
-- interrupts
@ -79,7 +80,7 @@ begin
process(I_clk)
begin
if rising_edge(I_clk) then
if rising_edge(I_clk) and I_halt = '0' then
if I_reset = '1' then
s_state <= "0000001";
next_s_state <= "0000001";
@ -93,11 +94,11 @@ begin
set_idata <= '0';
instTick <= '0';
else
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
case s_state is
when "0000001" => -- fetch
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
instTick <= '0';
if mem_cycles = 0 and mem_ready = '1' then
mem_execute <= '1';
@ -114,21 +115,28 @@ begin
end if;
end if;
when "0000010" => --- decode
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
s_state <= "0001000"; --E "0000100"; --R
when "0000100" => -- read -- DEPRECATED STAGE
s_state <= "0001000"; --E
when "0001000" => -- execute
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
--MEM/WB
-- if it's not a memory alu op, goto writeback
if (I_aluop(6 downto 2) = OPCODE_LOAD or
I_aluop(6 downto 2) = OPCODE_STORE) then
s_state <= "0010000"; -- MEM
-- elsif (I_aluop(6 downto 2) = OPCODE_SYSTEM) then
-- s_state <= "1001000"; -- alu stall
else
s_state <= "0100000"; -- WB
end if;
when "0010000" => -- mem
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
-- sometimes memory can be busy, if so we need to relook here
if mem_cycles = 0 and mem_ready = '1' then
mem_execute <= '1';
@ -155,28 +163,31 @@ begin
next_s_state <= "0000001"; --F
s_state <= "1000000"; --F
else
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
s_state <= "0000001"; --F
end if;
instTick <= '1';
when "1000000" => -- stalls
if I_int = '0' then
interrupt_was_inactive <= '1';
end if;
instTick <= '0';
-- interrupt stall
if interrupt_state = "001" then
-- give a cycle of latency
-- interrupt_state <= "010";
-- elsif interrupt_state = "010" then
-- -- sample input data for state?
-- O_idata <= I_int_mem_data;
-- set_idata <= '1';
-- interrupt_state <= "100";
-- elsif interrupt_state = "100" then
-- set_idata <= '0';
-- set PC to interrupt vector.
set_ipc <= '1';
interrupt_state <= "101";
elsif interrupt_state = "101" then
set_ipc <= '0';
interrupt_ack <= '0';
interrupt_state <= "101";
-- interrupt_ack <= '0';
elsif interrupt_state = "101" then
set_ipc <= '0';
interrupt_ack <= '0';
interrupt_state <= "111";
elsif interrupt_state = "111" then
interrupt_state <= "000";
s_state <= "0000001"; --F
end if;

View file

@ -6,7 +6,7 @@
-- This is the CPU interface required.
--
----------------------------------------------------------------------------------
-- Copyright 2016 Colin Riley
-- Copyright 2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -50,6 +50,7 @@ entity core is
MEM_I_dataReady : IN std_logic
; -- This debug output contains some internal state for debugging
O_halted: OUT std_logic;
O_DBG:out std_logic_vector(63 downto 0)
);
end core;
@ -68,6 +69,7 @@ architecture Behavioral of core is
COMPONENT control_unit
PORT (
I_clk : in STD_LOGIC;
I_halt: in STD_LOGIC;
I_reset : in STD_LOGIC;
I_aluop : in STD_LOGIC_VECTOR (6 downto 0);
O_state : out STD_LOGIC_VECTOR (6 downto 0);
@ -105,6 +107,7 @@ architecture Behavioral of core is
O_memOp : out STD_LOGIC_VECTOR(4 downto 0);
O_csrOP : out STD_LOGIC_VECTOR(4 downto 0);
O_csrAddr : out STD_LOGIC_VECTOR(11 downto 0);
O_trapExit: out STD_LOGIC;
O_int : out STD_LOGIC;
O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
I_int_ack: in STD_LOGIC
@ -126,6 +129,7 @@ architecture Behavioral of core is
O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
O_branchTarget : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
O_dataWriteReg : out STD_LOGIC;
O_lastPC: out STD_LOGIC_VECTOR(XLEN32M1 downto 0);
O_shouldBranch : out std_logic
);
@ -162,9 +166,16 @@ architecture Behavioral of core is
I_instRetTick : in STD_LOGIC;
-- mcause has a fast path in from other units
I_int_cause: in STD_LOGIC_VECTOR (XLENM1 downto 0);
I_int_pc: in STD_LOGIC_VECTOR (XLENM1 downto 0);
-- interrupt handling causes many data dependencies
-- mcause has a fast path in from other units
I_int_cause: in STD_LOGIC_VECTOR (XLENM1 downto 0);
I_int_pc: in STD_LOGIC_VECTOR (XLENM1 downto 0);
-- We need to know when an interrupt occurs as to perform the
-- relevant csr modifications. Same with exit.
I_int_entry: IN STD_LOGIC;
I_int_exit: IN STD_LOGIC;
-- Currently just feeds machine level CSR values
O_csr_status : out STD_LOGIC_VECTOR (XLENM1 downto 0);
O_csr_cause : out STD_LOGIC_VECTOR (XLENM1 downto 0);
@ -178,6 +189,8 @@ architecture Behavioral of core is
PORT (
I_clk : in STD_LOGIC;
I_reset : in STD_LOGIC;
I_nextPc : in STD_LOGIC_VECTOR (31 downto 0);
I_enMask : in STD_LOGIC_VECTOR (3 downto 0);
I_pc : in STD_LOGIC_VECTOR (31 downto 0);
I_int0 : in STD_LOGIC;
I_int_data0 : in STD_LOGIC_VECTOR (31 downto 0);
@ -187,8 +200,10 @@ architecture Behavioral of core is
O_int1_ack: out STD_LOGIC;
I_int2 : in STD_LOGIC;
I_int_data2 : in STD_LOGIC_VECTOR (31 downto 0);
O_int2_ack: out STD_LOGIC;
I_int3 : in STD_LOGIC;
I_int_data3 : in STD_LOGIC_VECTOR (31 downto 0);
O_int3_ack: out STD_LOGIC;
O_int : out STD_LOGIC;
O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
O_int_epc : out STD_LOGIC_VECTOR (31 downto 0)
@ -205,6 +220,7 @@ architecture Behavioral of core is
I_address : IN std_logic_vector(XLENM1 downto 0);
I_data : IN std_logic_vector(XLENM1 downto 0);
I_dataByteEn : IN std_logic_vector(1 downto 0);
I_signExtend : in STD_LOGIC;
O_data : OUT std_logic_vector(XLENM1 downto 0);
O_dataReady : OUT std_logic;
MEM_I_ready : IN std_logic;
@ -250,6 +266,7 @@ architecture Behavioral of core is
signal decoder_int: STD_LOGIC;
signal decoder_int_data: STD_LOGIC_VECTOR(XLENM1 downto 0);
signal decoder_int_ack: STD_LOGIC := '0';
signal decoder_trap_exit: STD_LOGIC := '0';
signal reg_en: std_logic := '0';
@ -268,6 +285,9 @@ architecture Behavioral of core is
signal PC : std_logic_vector(XLENM1 downto 0) := (others => '0');
signal PC_at_int : std_logic_vector(XLENM1 downto 0) := (others => '0');
signal lastPC_dec : std_logic_vector(XLENM1 downto 0) := (others => '0');
signal lastPC_alu : std_logic_vector(XLENM1 downto 0) := (others => '0');
signal nextPC_stall : std_logic_vector(XLENM1 downto 0) := (others => '0');
signal memctl_ready : std_logic;
signal memctl_execute : std_logic := '0';
@ -284,9 +304,12 @@ architecture Behavioral of core is
signal int_idata: STD_LOGIC_VECTOR(XLENM1 downto 0);
signal int_set_idata: STD_LOGIC;
signal int_enabled: std_logic := '0';
signal int_enabled: std_logic := '1'; --'0';
signal int_set_irpc: STD_LOGIC;
signal I_int_entry: STD_LOGIC := '0';
signal I_int_exit: STD_LOGIC := '0';
signal csru_int: STD_LOGIC;
signal csru_int_data: STD_LOGIC_VECTOR(XLENM1 downto 0);
signal csru_int_ack: STD_LOGIC := '0';
@ -315,8 +338,19 @@ architecture Behavioral of core is
signal lint_int: STD_LOGIC;
signal lint_int_data: STD_LOGIC_VECTOR(XLENM1 downto 0);
signal lint_enable_mask : STD_LOGIC_VECTOR (3 downto 0):= (others => '0');
signal external_int_ack : STD_LOGIC := '0';
signal dbg_data_line: STD_LOGIC_VECTOR(XLENM1 downto 0);
signal is_illegal :std_logic:='0';
signal should_halt: STD_LOGIC := '0';
begin
should_halt <= I_halt;
O_halted <= should_halt;
core_clock <= I_clk;
memctl: mem_controller PORT MAP (
@ -329,6 +363,7 @@ begin
I_address => memctl_address,
I_data => memctl_in_data,
I_dataByteEn => memctl_dataByteEn,
I_signExtend => memctl_signExtend,
O_data => memctl_out_data,
O_dataReady => memctl_dataReady,
@ -353,6 +388,7 @@ begin
control: control_unit PORT MAP (
I_clk => core_clock,
I_reset => I_reset,
I_halt => should_halt,
I_aluop => aluop,
I_int => lint_int,
@ -384,6 +420,7 @@ begin
O_memOp => memOp,
O_csrOp => csru_csrOp,
O_csrAddr => csru_csrAddr,
O_trapExit => decoder_trap_exit,
-- This unit can raise exceptions
O_int => decoder_int,
O_int_data => decoder_int_data,
@ -404,6 +441,7 @@ begin
O_dataResult => dataResult,
O_branchTarget => branchTarget,
O_dataWriteReg => dataWriteReg,
O_lastPC => lastPC_alu,
O_shouldBranch => shouldBranch
);
@ -440,6 +478,9 @@ begin
I_int_cause => lint_int_data,
I_int_pc => PC_at_int,
I_int_entry => I_int_entry,
I_int_exit => I_int_exit,
O_csr_status => csr_status,
O_csr_tvec => csr_tvec,
O_csr_cause => csr_cause,
@ -450,22 +491,45 @@ begin
lint: lint_unit PORT MAP (
I_clk => core_clock,
I_reset => lint_reset,
I_pc => PC,
I_nextPc => nextPC_stall,
I_enMask => lint_enable_mask,
I_pc => lastPC_dec,
I_int0 => decoder_int,
I_int_data0 => decoder_int_data,
O_int0_ack => decoder_int_ack,
I_int1 => csru_int,
I_int_data1 => csru_int_data,
O_int1_ack => csru_int_ack,
I_int2 => lint_nothing,
I_int_data2 => lint_nothing_data,
I_int2 => I_int,
I_int_data2 => I_int_data,
O_int2_ack => external_int_ack,
I_int3 => lint_nothing,
I_int_data3 => lint_nothing_data,
O_int => lint_int,
O_int_data => lint_int_data,
O_int_epc => PC_at_int
O_int_data => lint_int_data--,
-- O_int_epc => PC_at_int
);
O_int_ack <= external_int_ack;
state_latcher: process(core_clock)
begin
if rising_edge(core_clock) then
if en_decode = '1' then
lastPC_dec <= PC;
end if;
if state(6) = '1' then
nextPC_stall <= PC;
end if;
end if;
end process;
-- Register file controls
reg_en <= en_decode or en_regwrite;
@ -474,7 +538,7 @@ begin
-- These are the pipeline stage enable bits
en_fetch <= state(0);
en_decode <= state(1);
en_alu <= '0' when (aluop(6 downto 2) = OPCODE_SYSTEM and aluFunc(2 downto 0) /= "000") else state(3);
en_alu <= state(3);
en_csru <= state(3) when (aluop(6 downto 2) = OPCODE_SYSTEM and aluFunc(2 downto 0) /= "000") else '0';
en_memory <= state(4);
en_regwrite <= state(5);
@ -486,22 +550,51 @@ begin
PCU_OP_INC when shouldBranch = '0' and state(5) = '1' else
PCU_OP_ASSIGN when PCintvec = '1' else
PCU_OP_NOP;
-- this is lint interrupt enable for consuming the interrupt
-- unused/external/crsu/decoder
-- Only accept external on ALU stage to prevent issues with externals taking decode int's in fetch cycles
-- externals are also programmable via csr register bit
lint_enable_mask <= '0' & (csr_status(3)and state(3)) & '1' & '1';
-- interrupts are controlled by mstatus.mie - this is proper control unit acceptance
int_enabled <= '1' when (lint_int_data(31) = '0' and lint_int = '1') else csr_status(3);
PC_at_int <= branchTarget when (shouldBranch = '1' and lint_int_data(31) = '1' and state(6) = '1' and lint_int = '1') else PC when (lint_int_data(31) = '1' and lint_int = '1') else lastPC_dec;
-- On Interrupt service entry, CSRs need some maintenance.
-- We need to strobe the CSR unit on this event.
I_int_entry <= PCintvec;
-- To detect exit, we strobe using the ALU enable with the decoder trap request bit
I_int_exit <= decoder_trap_exit and en_alu;
-- The input PC is just always the branch target output from ALU
-- todo: tvec needs modifiec for vectored exceptions
-- todo: tvec needs modified for vectored exceptions
in_pc <= csr_tvec when PCintvec = '1' else branchTarget;
-- input data from the register file, or use immediate if the OP specifies it
csru_dataIn <= dataIMM when csru_csrOp(CSR_OP_BITS_IMM) = '1' else dataA;
dbg_data_line <= registerWriteData when state(5) = '1' else memctl_address;
--dbg_data_line can be used to aid debugging cpu issues using trace dumps.
--dbg_data_line <= csr_tvec when memctl_execute = '1' else csru_dataIn when en_csru = '1' else registerWriteData when state(5) = '1' else X"000000" & "000" & selD when state(3) = '1' else instruction when state(1)='1' else memctl_address;
--dbg_data_line <= memctl_address when memctl_execute = '1' else MEM_I_data;
dbg_data_line <= X"ABCDEF01" when (decoder_int_data = EXCEPTION_INSTRUCTION_ILLEGAL and X"00000010" = csr_epc ) else csru_dataIn when en_csru = '1' else registerWriteData when state(5) = '1' else X"000000" & "000" & selD when state(3) = '1' else instruction when state(1)='1' else memctl_address;
--dbg_data_line <= PC_at_int;--registerWriteData when state(5) = '1' else X"000000" & "000" & selD when state(3) = '1' else instruction when state(1)='1' else csr_epc when ( lint_reset = '1') else memctl_address;
is_illegal <= '1' when decoder_int_data = EXCEPTION_INSTRUCTION_ILLEGAL else '0';
-- The debug output just allows some internal state to be visible outside the core black box
O_DBG <= "0000" & "00" & memctl_dataReady & MEM_I_dataReady & dataWriteReg & "0" & lint_reset & lint_int & "00" & decoder_int & csru_int & "000" & aluop(6 downto 2) & "0" & state & dbg_data_line;-- & registerWriteData(15 downto 0);
-- byte 1 - memctrl&dataready
-- byte 2 - dataWriteReg, lint_reset, lint_int, decoder and csru_int
-- byte 2 - dataWriteReg, int_en, lint_reset, lint_int, interrupt_type_ decoder and csru_int
-- byte 3 - aluop
-- byte 4 - state
-- uint32 - data
O_DBG <= "0000" & "00" & memctl_dataReady & MEM_I_dataReady &
-- dataWriteReg & int_enabled & lint_reset & lint_int & lint_int_data(31) & PCintvec & decoder_int & decoder_int_ack &--&csru_int & --I_int & --
dataWriteReg & int_enabled & lint_reset & lint_int & I_int & external_int_ack & decoder_int & decoder_int_ack &--&csru_int & --I_int & --
is_illegal & "00" & aluop(6 downto 2) &
"0" & state &
dbg_data_line;
-- Below statements are for memory interface use.
memctl_address <= dataResult when en_memory = '1' else PC;
@ -510,12 +603,14 @@ begin
memctl_in_data <= dataB;
memctl_dataWe <= '1' when en_memory = '1' and memOp(4 downto 3) = "11" else '0';
memctl_size <= memOp(1 downto 0);
memctl_signExtend <= memOp(2);
memctl_signExtend <= not memOp(2);
-- This chooses to write registers with memory data or ALU/csr data
registerWriteData <= memctl_out_data when memOp(4 downto 3) = "10" else csru_dataOut when aluop(6 downto 2) = OPCODE_SYSTEM else dataResult;
registerWriteData <= memctl_out_data when memOp(4 downto 3) = "10" else dataB when (aluop(6 downto 2) = OPCODE_STORE ) else csru_dataOut when (aluop(6 downto 2) = OPCODE_SYSTEM and aluFunc(2 downto 0) /= "000") else dataResult;
-- The instructions are delivered from memctl
-- FIXME: The instruction needs LATCHED. Any change to data input at a certain time
-- can confuse the pipeline and get it into an inconsistent state.
instruction <= memctl_out_data;
end Behavioral;

View file

@ -3,7 +3,7 @@
-- Description: CSR unit RV32I
--
----------------------------------------------------------------------------------
-- Copyright 2018 Colin Riley
-- Copyright 2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -34,9 +34,16 @@ entity csr_unit is
O_int : out STD_LOGIC;
O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
I_instRetTick : in STD_LOGIC;
-- interrupt handling causes many data dependencies
-- mcause has a fast path in from other units
I_int_cause: in STD_LOGIC_VECTOR (XLENM1 downto 0);
I_int_pc: in STD_LOGIC_VECTOR (XLENM1 downto 0);
-- We need to know when an interrupt occurs as to perform the
-- relevant csr modifications. Same with exit.
I_int_entry: IN STD_LOGIC;
I_int_exit: IN STD_LOGIC;
-- Currently just feeds machine level CSR values
O_csr_status : out STD_LOGIC_VECTOR (XLENM1 downto 0);
O_csr_cause : out STD_LOGIC_VECTOR (XLENM1 downto 0);
@ -95,17 +102,24 @@ constant CSR_ADDR_MARCHID: STD_LOGIC_VECTOR (11 downto 0) := X"F12";
constant CSR_ADDR_MIMPID: STD_LOGIC_VECTOR (11 downto 0) := X"F13";
constant CSR_ADDR_MHARDID: STD_LOGIC_VECTOR (11 downto 0) := X"F14";
-- Will allow some other CSRS to make for easier running of third party sw
constant CSR_ADDR_VEXRISC_IRQ_MASK: STD_LOGIC_VECTOR (11 downto 0) := X"bc0";
constant CSR_ADDR_VEXRISC_IRQ_PENDING: STD_LOGIC_VECTOR (11 downto 0) := X"fc0";
signal csr_cycles: STD_LOGIC_VECTOR(63 downto 0) := (others => '0');
signal csr_instret: STD_LOGIC_VECTOR(63 downto 0) := (others => '0');
signal csr_mstatus : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_mstatus : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";-- X"00001800"; -- MIE default 1
signal csr_mie : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_mtvec : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000010";
signal csr_mtvec : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000004";-- X"00000010";
signal csr_mscratch : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_mepc : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_mcause : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_mtval : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_mip : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_vexrisc_irq_mask : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal csr_vexrisc_irq_pending : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
signal curr_csr_value: STD_LOGIC_VECTOR(XLENM1 downto 0) := (others=> '0');
signal next_csr_value: STD_LOGIC_VECTOR(XLENM1 downto 0) := (others=> '0');
@ -165,15 +179,28 @@ begin
datamain: process (I_clk, I_en)
begin
if rising_edge(I_clk) then
-- fastpath mcause write
if (I_int_cause /= csr_mcause) then
csr_mcause <= I_int_cause;
end if;
if (I_int_pc /= csr_mepc) then
csr_mepc <= I_int_pc;
end if;
if I_en = '1' and opState = 0 then
if I_int_entry = '1' then
-- on entry:
-- mstatus.mpie = mstatus.mie
csr_mstatus(7) <= csr_mstatus(3);
-- mstatus.mie = 0
csr_mstatus(3) <= '0';
-- mstatus.mpp = current privilege mode
csr_mstatus(12 downto 11) <= "11";
csr_mcause <= I_int_cause;
csr_mepc <= I_int_pc;
elsif I_int_exit = '1' then
-- privilege set to mstatus.mpp
-- mstatus.mie = mstatus.mpie
csr_mstatus(3) <= csr_mstatus(7);
csr_mstatus(7) <= '1';
csr_mstatus(12 downto 11) <= "00";
-- interrupt data changes take all priority
elsif I_en = '1' and opState = 0 then
csr_op <= I_csrOp;
case I_csrAddr is
when CSR_ADDR_MVENDORID =>
@ -193,9 +220,18 @@ begin
curr_csr_value <= csr_mtvec;
when CSR_ADDR_MIE =>
curr_csr_value <= csr_mie;
when CSR_ADDR_MIP =>
curr_csr_value <= csr_mip;
when CSR_ADDR_MCAUSE =>
curr_csr_value <= csr_mcause;
curr_csr_value <= csr_mcause;
when CSR_ADDR_MEPC =>
curr_csr_value <= csr_mepc;
when CSR_ADDR_VEXRISC_IRQ_PENDING =>
curr_csr_value <= csr_vexrisc_irq_pending;
when CSR_ADDR_VEXRISC_IRQ_MASK =>
curr_csr_value <= csr_vexrisc_irq_mask;
when CSR_ADDR_CYCLE =>
curr_csr_value <= csr_cycles(31 downto 0);
when CSR_ADDR_CYCLEH =>
@ -258,7 +294,17 @@ begin
when CSR_ADDR_MTVEC =>
csr_mtvec <= next_csr_value;
when CSR_ADDR_MIE =>
csr_mie <= next_csr_value;
csr_mie <= next_csr_value;
when CSR_ADDR_MIP =>
csr_mip <= next_csr_value;
when CSR_ADDR_MEPC =>
csr_mepc <= next_csr_value;
when CSR_ADDR_VEXRISC_IRQ_PENDING =>
csr_vexrisc_irq_pending <= next_csr_value;
when CSR_ADDR_VEXRISC_IRQ_MASK =>
csr_vexrisc_irq_mask <= next_csr_value;
when others =>
end case;
end if;

View file

@ -3,7 +3,7 @@
-- Description: Local Interrupt unit
--
----------------------------------------------------------------------------------
-- Copyright 2018 Colin Riley
-- Copyright 2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -27,7 +27,9 @@ use work.constants.all;
entity lint_unit is
Port ( I_clk : in STD_LOGIC;
I_reset : in STD_LOGIC;
I_nextPc : in STD_LOGIC_VECTOR (31 downto 0);
I_pc : in STD_LOGIC_VECTOR (31 downto 0);
I_enMask : in STD_LOGIC_VECTOR (3 downto 0);
I_int0 : in STD_LOGIC;
I_int_data0 : in STD_LOGIC_VECTOR (31 downto 0);
O_int0_ack: out STD_LOGIC;
@ -36,8 +38,10 @@ entity lint_unit is
O_int1_ack: out STD_LOGIC;
I_int2 : in STD_LOGIC;
I_int_data2 : in STD_LOGIC_VECTOR (31 downto 0);
O_int2_ack: out STD_LOGIC;
I_int3 : in STD_LOGIC;
I_int_data3 : in STD_LOGIC_VECTOR (31 downto 0);
O_int3_ack: out STD_LOGIC;
O_int : out STD_LOGIC;
O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
O_int_epc : out STD_LOGIC_VECTOR (31 downto 0)
@ -54,6 +58,9 @@ signal int0_ack: std_logic := '0';
signal int1_ack: std_logic := '0';
signal int2_ack: std_logic := '0';
signal int3_ack: std_logic := '0';
signal reset_counter: integer := 0;
begin
O_int <= actual_int;
@ -62,6 +69,8 @@ begin
O_int0_ack <= int0_ack;
O_int1_ack <= int1_ack;
O_int2_ack <= int2_ack;
O_int3_ack <= int3_ack;
-- This simply filters one of the 4 int sources to a single one in
-- decreasing priority, latching the data until a reset.
@ -69,49 +78,40 @@ begin
begin
if rising_edge(I_clk) then
if I_reset = '1' then
actual_int <= '0';
reset_counter <= 1;
int0_ack <= '0';
int1_ack <= '0';
int2_ack <= '0';
int3_ack <= '0';
elsif I_int0 = '1' then
actual_int <= '1';
actual_int_data <= I_int_data0;
int0_ack <= '1';
if (I_int_data0(31) = '1') then
actual_int_epc <= std_logic_vector(signed( I_PC) + 4);
else
actual_int_epc <= I_PC;
elsif reset_counter = 1 then
reset_counter <= 2;
elsif reset_counter = 2 then
reset_counter <= 3;
elsif reset_counter = 3 then
actual_int <= '0';
reset_counter <= 0;
elsif reset_counter = 0 and actual_int = '0' then
if I_enMask(0) = '1' and I_int0 = '1' and int0_ack = '0' then
actual_int <= '1';
actual_int_data <= I_int_data0;
int0_ack <= '1';
elsif I_enMask(1) = '1' and I_int1 = '1' and int1_ack = '0'then
actual_int <= '1';
actual_int_data <= I_int_data1;
int1_ack <= '1';
elsif I_enMask(2) = '1' and I_int2 = '1' and int2_ack = '0' then
actual_int <= '1';
actual_int_data <= I_int_data2;
int2_ack <= '1';
elsif I_enMask(3) = '1' and I_int3 = '1' and int3_ack = '0'then
actual_int <= '1';
actual_int_data <= I_int_data3;
int3_ack <= '1';
end if;
end if;
elsif I_int1 = '1' then
actual_int <= '1';
actual_int_data <= I_int_data1;
int1_ack <= '1';
if (I_int_data1(31) = '1') then
actual_int_epc <= std_logic_vector(signed( I_PC) + 4);
else
actual_int_epc <= I_PC;
end if;
elsif I_int2 = '1' then
actual_int <= '1';
actual_int_data <= I_int_data2;
int2_ack <= '1';
if (I_int_data2(31) = '1') then
actual_int_epc <= std_logic_vector(signed( I_PC) + 4);
else
actual_int_epc <= I_PC;
end if;
elsif I_int3 = '1' then
actual_int <= '1';
actual_int_data <= I_int_data3;
int3_ack <= '1';
if (I_int_data3(31) = '1') then
actual_int_epc <= std_logic_vector(signed( I_PC) + 4);
else
actual_int_epc <= I_PC;
end if;
end if;
end if;
end process;
end Behavioral;

View file

@ -6,7 +6,7 @@
-- MEM_ signals are expected to be exposted to any SoC fabric.
--
----------------------------------------------------------------------------------
-- Copyright 2016 Colin Riley
-- Copyright 2016,2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -23,6 +23,8 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
library work;
use work.constants.all;
@ -38,6 +40,7 @@ entity mem_controller is
I_address : in STD_LOGIC_VECTOR (XLENM1 downto 0);
I_data : in STD_LOGIC_VECTOR (XLENM1 downto 0);
I_dataByteEn : in STD_LOGIC_VECTOR(1 downto 0);
I_signExtend : in STD_LOGIC;
O_data : out STD_LOGIC_VECTOR (XLENM1 downto 0);
O_dataReady: out STD_LOGIC;
@ -57,6 +60,8 @@ architecture Behavioral of mem_controller is
signal we : std_logic := '0';
signal addr : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";
signal indata: STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";
signal outdata: STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";
signal byteEnable: STD_LOGIC_VECTOR ( 1 downto 0) := "11";
signal cmd : STD_LOGIC := '0';
signal state: integer := 0;
@ -80,6 +85,7 @@ begin
byteEnable <= I_dataByteEn;
cmd <= '1';
O_dataReady <= '0';
outdata <= X"ABCDEFEE";
if I_dataWe = '0' then
state <= 3;-- read
else
@ -92,7 +98,18 @@ begin
cmd <= '0';
if MEM_I_dataReady = '1' then
O_dataReady <= '1';
O_data <= MEM_I_data;
-- sign extend, if required
if I_signExtend = '1' then
if I_dataByteEn = F2_MEM_LS_SIZE_W then
outdata <= MEM_I_data;
elsif I_dataByteEn = F2_MEM_LS_SIZE_H then
outdata <= std_logic_vector(resize(signed(MEM_I_data(15 downto 0)), XLEN));
elsif I_dataByteEn = F2_MEM_LS_SIZE_B then
outdata <= std_logic_vector(resize(signed(MEM_I_data(7 downto 0)), XLEN));
end if;
else
outdata <= MEM_I_data;
end if;
state <= 2;
end if;
elsif state = 2 then
@ -103,6 +120,7 @@ begin
end if;
end process;
O_data <= outdata;
O_ready <= ( MEM_I_ready and not I_execute ) when state = 0 else '0';
MEM_O_cmd <= cmd;

View file

@ -5,7 +5,7 @@
-- Simple black box for holding and manipulating the PC
--
----------------------------------------------------------------------------------
-- Copyright 2016 Colin Riley
-- Copyright 2016,2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -46,9 +46,6 @@ begin
if rising_edge(I_clk) then
case I_nPCop is
when PCU_OP_NOP => -- NOP, keep PC the same/halt
-- if I_intVec = '1' then -- in a NOP, you can get intterupts. check.
-- current_pc <= ADDR_INTVEC;-- set PC to interrupt vector;
-- end if;
when PCU_OP_INC => -- increment
current_pc <= std_logic_vector(unsigned(current_pc) + 4); -- 32bit byte addressing
when PCU_OP_ASSIGN => -- set from external input

View file

@ -40,7 +40,8 @@ end register_set;
architecture Behavioral of register_set is
type store_t is array (0 to 31) of std_logic_vector(XLENM1 downto 0);
signal regs: store_t := (others => X"00000000");
signal regsA: store_t := (others => X"00000000");
signal regsB: store_t := (others => X"00000000");
signal dataAout: STD_LOGIC_VECTOR (XLENM1 downto 0) := (others=>'0');
signal dataBout: STD_LOGIC_VECTOR (XLENM1 downto 0) := (others=>'0');
begin
@ -48,16 +49,17 @@ begin
process(I_clk, I_en)
begin
if rising_edge(I_clk) and I_en='1' then
dataAout <= regs(to_integer(unsigned(I_selRS1)));
dataBout <= regs(to_integer(unsigned(I_selRS2)));
dataAout <= regsA(to_integer(unsigned(I_selRS1)));
dataBout <= regsB(to_integer(unsigned(I_selRS2)));
if (I_we = '1') then
regs(to_integer(unsigned(I_selD))) <= I_dataD;
regsA(to_integer(unsigned(I_selD))) <= I_dataD;
regsB(to_integer(unsigned(I_selD))) <= I_dataD;
end if;
end if;
end process;
O_dataA <= dataAout;
O_dataB <= dataBout;
O_dataA <= dataAout when I_selRS1 /= "00000" else X"00000000";
O_dataB <= dataBout when I_selRS2 /= "00000" else X"00000000";
end Behavioral;

View file

@ -3,7 +3,7 @@
-- Description: ALU unit suitable for RV32I operational use
--
----------------------------------------------------------------------------------
-- Copyright 2016 Colin Riley
-- Copyright 2016,2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -40,6 +40,7 @@ entity alu_RV32I is
O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
O_branchTarget : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
O_dataWriteReg : out STD_LOGIC;
O_lastPC: out STD_LOGIC_VECTOR(XLEN32M1 downto 0);
O_shouldBranch : out std_logic
);
end alu_RV32I;
@ -51,11 +52,13 @@ architecture Behavioral of alu_RV32I is
signal s_branchTarget : STD_LOGIC_VECTOR (XLEN32M1 downto 0) := (others => '0');
signal s_result: STD_LOGIC_VECTOR(XLEN32M1+2 downto 0) := (others => '0');
signal s_shouldBranch: STD_LOGIC := '0';
signal s_lastPC: STD_LOGIC_VECTOR(XLEN32M1 downto 0) := (others => '0');
begin
process (I_clk, I_en)
begin
if rising_edge(I_clk) and I_en = '1' then
s_lastPC <= I_PC;
O_dataWriteReg <= I_dataDwe;
case I_aluop is
when OPCODE_OPIMM =>
@ -166,6 +169,9 @@ begin
s_branchTarget <= I_epc;
s_shouldBranch <= '1';
s_result(31 downto 0) <= std_logic_vector(signed( I_PC) + 4);
elsif I_aluFunc(2 downto 0) /= F3_PRIVOP then
-- do not branch on CSR unit work
s_shouldBranch <= '0';
end if;
when OPCODE_LUI =>
s_shouldBranch <= '0';
@ -232,5 +238,6 @@ begin
O_dataResult <= s_result(XLEN32M1 downto 0);
O_shouldBranch <= s_shouldBranch;
O_branchTarget <= s_branchTarget;
O_lastPC <= s_lastPC;
end Behavioral;

View file

@ -3,7 +3,7 @@
-- Description: decoder unit RV32I
--
----------------------------------------------------------------------------------
-- Copyright 2016 Colin Riley
-- Copyright 2016,2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@ -39,6 +39,7 @@ entity decoder_RV32 is
O_memOp : out STD_LOGIC_VECTOR(4 downto 0); -- Memory operation
O_csrOP : out STD_LOGIC_VECTOR(4 downto 0); -- CSR operations
O_csrAddr : out STD_LOGIC_VECTOR(11 downto 0); -- CSR address
O_trapExit: out STD_LOGIC; -- request to exit trap handler
O_int : out STD_LOGIC; -- is there a trap?
O_int_data : out STD_LOGIC_VECTOR (31 downto 0); -- trap descriptor
I_int_ack: in STD_LOGIC -- our int is now being serviced
@ -46,17 +47,27 @@ entity decoder_RV32 is
end decoder_RV32;
architecture Behavioral of decoder_RV32 is
signal s_trapExit: STD_LOGIC := '0';
signal s_csrOP : STD_LOGIC_VECTOR(4 downto 0) := (others=> '0');
signal s_csrAddr : STD_LOGIC_VECTOR(11 downto 0) := (others=> '0');
signal s_int : STD_LOGIC := '0';
signal s_intdata: STD_LOGIC_VECTOR(31 downto 0) := (others=> '0');
begin
-- Register selects for reads are async
O_int <= s_int;
O_int_data <= s_intdata;
O_csrOP <= s_csrOP;
O_csrAddr <= s_csrAddr;
O_trapExit <= s_trapExit;
-- Register selects for reads are async
O_selRS1 <= I_dataInst(R1_START downto R1_END);
O_selRS2 <= I_dataInst(R2_START downto R2_END);
process (I_clk, I_en)
begin
if rising_edge(I_clk) and I_int_ack = '1' then
O_int <= '0';
end if;
if rising_edge(I_clk) and I_en = '1' then
if rising_edge(I_clk) then
if I_en = '1' then
O_selD <= I_dataInst(RD_START downto RD_END);
@ -67,19 +78,25 @@ begin
case I_dataInst(OPCODE_START downto OPCODE_END_2) is
when OPCODE_LUI =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '1';
O_memOp <= "00000";
O_dataIMM <= I_dataInst(IMM_U_START downto IMM_U_END)
& "000000000000";
when OPCODE_AUIPC =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '1';
O_memOp <= "00000";
O_dataIMM <= I_dataInst(IMM_U_START downto IMM_U_END)
& "000000000000";
when OPCODE_JAL =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
if I_dataInst(RD_START downto RD_END) = "00000" then
O_regDwe <= '0';
else
@ -92,7 +109,9 @@ begin
O_dataIMM <= "000000000000" & I_dataInst(19 downto 12) & I_dataInst(20) & I_dataInst(30 downto 21) & '0';
end if;
when OPCODE_JALR =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
if I_dataInst(RD_START downto RD_END) = "00000" then
O_regDwe <= '0';
else
@ -105,7 +124,9 @@ begin
O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END);
end if;
when OPCODE_OPIMM =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '1';
O_memOp <= "00000";
if I_dataInst(IMM_U_START) = '1' then
@ -115,20 +136,50 @@ begin
end if;
when OPCODE_OP =>
O_int <= '0';
O_regDwe <= '1';
O_memOp <= "00000";
if I_dataInst(FUNCT7_START downto FUNCT7_END) = "0000001" then
-- RV M EXTENSION - NOT SUPPORTED!
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '1';
s_intdata <= EXCEPTION_INSTRUCTION_ILLEGAL;
O_regDwe <= '0';
O_memOp <= "00000";
else
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '1';
O_memOp <= "00000";
end if;
when OPCODE_LOAD =>
O_int <= '0';
O_regDwe <= '1';
O_memOp <= "10" & I_dataInst(FUNCT3_START downto FUNCT3_END);
if I_dataInst(IMM_U_START) = '1' then
O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END);
-- Load's opcode is all 0s - but the first two bits of the word should be '11'
-- we check this here, because if we do not, null instructions will be treated as loads...
if I_dataInst(1 downto 0) = "11" then
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '1';
O_memOp <= "10" & I_dataInst(FUNCT3_START downto FUNCT3_END);
if I_dataInst(IMM_U_START) = '1' then
O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END);
else
O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END);
end if;
else
O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END);
-- likely a null instruction - fault!
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '1'; ---------------
s_intdata <= EXCEPTION_INSTRUCTION_ILLEGAL;
O_memOp <= "00000";
O_regDwe <= '0';
O_dataIMM <= I_dataInst(IMM_I_START downto IMM_S_B_END)
& "0000000";
end if;
when OPCODE_STORE =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '0';
O_memOp <= "11" & I_dataInst(FUNCT3_START downto FUNCT3_END);
if I_dataInst(IMM_U_START) = '1' then
@ -137,7 +188,9 @@ begin
O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_S_A_START downto IMM_S_A_END) & I_dataInst(IMM_S_B_START downto IMM_S_B_END);
end if;
when OPCODE_BRANCH =>
O_int <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '0';
O_memOp <= "00000";
if I_dataInst(IMM_U_START) = '1' then
@ -146,10 +199,12 @@ begin
O_dataIMM <= X"0000" & "0000" & I_dataInst(7) & I_dataInst(30 downto 25) & I_dataInst(11 downto 8) & '0';
end if;
when OPCODE_MISCMEM =>
O_int <= '0';
O_regDwe <= '0';
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '0';
O_memOp <= "01000";
O_dataIMM <= I_dataInst;
O_dataIMM <= I_dataInst;
when OPCODE_SYSTEM =>
O_memOp <= "00000";
if I_dataInst(FUNCT3_START downto FUNCT3_END) = F3_PRIVOP then
@ -157,56 +212,69 @@ begin
case I_dataInst(IMM_I_START downto IMM_I_END) is
when IMM_I_SYSTEM_ECALL =>
-- raise trap, save pc, perform requiredCSR operations
O_int <= '1';
O_int_data <= EXCEPTION_INT_MACHINE_SOFTWARE;
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '1';
O_regDwe <= '0';
s_intdata <= EXCEPTION_ENVIRONMENT_CALL_FROM_MMODE;
--todo: Priv level needs checked as to mask this to user/supervisor/machine level
when IMM_I_SYSTEM_EBREAK =>
O_int <= '1';
O_int_data <= EXCEPTION_BREAKPOINT;
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '1';
s_intdata <= EXCEPTION_BREAKPOINT;
O_regDwe <= '0';
when F7_PRIVOP_MRET & R2_PRIV_RET =>
O_int <= '0';
s_trapExit <= '1';
s_csrOP <= "00000";
s_int <= '0';
O_regDwe <= '0';
-- return from interrupt. implement as a branch - alu will branch to epc.
when others =>
end case;
else
O_int <= '0';
s_trapExit <= '0';
s_int <= '0';
-- CSR
-- The immediate output is the zero-extended R1 value for Imm-form CSR ops
O_dataIMM <= X"000000" & "000" & I_dataInst(R1_START downto R1_END);
-- The 12bit immediate in the instruction forms the csr address.
O_csrAddr <= I_dataInst(IMM_I_START downto IMM_I_END);
s_csrAddr <= I_dataInst(IMM_I_START downto IMM_I_END);
-- is there a destination? if not, CSR is not read
if I_dataInst(RD_START downto RD_END) = "00000" then
O_csrOP(0) <= '0';
s_csrOP(0) <= '0';
O_regDwe <= '0';
else
O_regDwe <= '1';
O_csrOP(0) <= '1';
s_csrOP(0) <= '1';
end if;
-- is there source data? if not, CSR value is not written
if I_dataInst(R1_START downto R1_END) = "00000" then
O_csrOP(1) <= '0';
s_csrOP(1) <= '0';
else
O_csrOP(1) <= '1';
s_csrOP(1) <= '1';
end if;
O_csrOp(4 downto 2) <= I_dataInst(FUNCT3_START downto FUNCT3_END);
s_csrOp(4 downto 2) <= I_dataInst(FUNCT3_START downto FUNCT3_END);
end if;
when others =>
O_int <= '1';
O_int_data <= EXCEPTION_INSTRUCTION_ILLEGAL;
s_trapExit <= '0';
s_csrOP <= "00000";
s_int <= '1'; ---------------
s_intdata <= EXCEPTION_INSTRUCTION_ILLEGAL;
O_memOp <= "00000";
O_regDwe <= '0';
O_dataIMM <= I_dataInst(IMM_I_START downto IMM_S_B_END)
& "0000000";
end case;
elsif I_int_ack = '1' then
s_int <= '0';
end if;
end if;
end process;
end Behavioral;