URL
https://opencores.org/ocsvn/neorv32/neorv32/trunk
Subversion Repositories neorv32
Compare Revisions
- This comparison shows the changes necessary to convert path
/neorv32/trunk
- from Rev 24 to Rev 25
- ↔ Reverse comparison
Rev 24 → Rev 25
/docs/figures/neorv32_cpu.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/docs/figures/neorv32_processor.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/docs/NEORV32.pdf
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/rtl/core/neorv32_cpu_alu.vhd
127,11 → 127,11
when others => opa <= csr_i; |
end case; |
-- opb (second ALU input operand) -- |
case ctrl_i(ctrl_alu_opb_mux_msb_c downto ctrl_alu_opb_mux_lsb_c) is |
when "00" => opb <= rs2_i; |
when "01" => opb <= imm_i; |
when others => opb <= rs1_i; |
end case; |
if (ctrl_i(ctrl_alu_opb_mux_c) = '0') then |
opb <= rs2_i; |
else |
opb <= imm_i; |
end if; |
-- opc (second operand for comparison and SUB) -- |
if (ctrl_i(ctrl_alu_opc_mux_c) = '0') then |
opc <= imm_i; |
251,7 → 251,7
|
-- ALU Function Select -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
alu_function_mux: process(ctrl_i, opa, opb, add_res, sub_res, cmp_less, shifter) |
alu_function_mux: process(ctrl_i, opa, opb, add_res, sub_res, cmp_less, shifter.sreg) |
begin |
case ctrl_i(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) is |
when alu_cmd_xor_c => alu_res <= opa xor opb; |
/rtl/core/neorv32_cpu_control.vhd
562,13 → 562,9
|
-- CPU Control Bus Output ----------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
ctrl_output: process(ctrl, execute_engine, fetch_engine, trap_ctrl, csr, bus_fast_ir) |
ctrl_output: process(ctrl, fetch_engine, trap_ctrl, csr, bus_fast_ir) |
begin |
ctrl_o <= ctrl; |
-- direct output of register addresses -- |
ctrl_o(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c) <= execute_engine.i_reg(instr_rd_msb_c downto instr_rd_lsb_c); |
ctrl_o(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c) <= execute_engine.i_reg(instr_rs1_msb_c downto instr_rs1_lsb_c); |
ctrl_o(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c) <= execute_engine.i_reg(instr_rs2_msb_c downto instr_rs2_lsb_c); |
-- fast bus access requests -- |
ctrl_o(ctrl_bus_if_c) <= ctrl(ctrl_bus_if_c) or bus_fast_ir; |
-- bus error control -- |
582,7 → 578,6
execute_engine_fsm_comb: process(execute_engine, fetch_engine, ipb, trap_ctrl, csr, ctrl, csr_acc_valid, |
alu_add_i, alu_wait_i, bus_d_wait_i, ma_load_i, be_load_i, ma_store_i, be_store_i) |
variable alu_immediate_v : std_ulogic; |
variable alu_operation_v : std_ulogic_vector(2 downto 0); |
variable rs1_is_r0_v : std_ulogic; |
begin |
-- arbiter defaults -- |
625,8 → 620,11
ctrl_nxt(ctrl_alu_shift_ar_c) <= execute_engine.i_reg(30); -- is arithmetic shift |
ctrl_nxt(ctrl_bus_size_lsb_c) <= execute_engine.i_reg(instr_funct3_lsb_c+0); -- transfer size lsb (00=byte, 01=half-word) |
ctrl_nxt(ctrl_bus_size_msb_c) <= execute_engine.i_reg(instr_funct3_lsb_c+1); -- transfer size msb (10=word, 11=?) |
ctrl_nxt(ctrl_cp_cmd2_c downto ctrl_cp_cmd0_c) <= execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c); -- CP operation |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_muldiv_c; -- only CP0 (MULDIV) implemented yet |
ctrl_nxt(ctrl_cp_cmd2_c downto ctrl_cp_cmd0_c) <= execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c); -- CP operation |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_muldiv_c; -- only CP0 (MULDIV) implemented yet |
ctrl_nxt(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c) <= ctrl(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c); -- keep rd addr |
ctrl_nxt(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c) <= ctrl(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c); -- keep rs1 addr |
ctrl_nxt(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c) <= ctrl(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c); -- keep rs2 addr |
|
-- is immediate operation? -- |
alu_immediate_v := '0'; |
634,24 → 632,6
alu_immediate_v := '1'; |
end if; |
|
-- alu operation re-coding -- |
case execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) is |
when funct3_subadd_c => -- SUB / ADD(I) |
if (alu_immediate_v = '0') and (execute_engine.i_reg(instr_funct7_msb_c-1) = '1') then -- not immediate and funct7 = SUB |
alu_operation_v := alu_cmd_sub_c; |
else |
alu_operation_v := alu_cmd_add_c; |
end if; |
when funct3_sll_c => alu_operation_v := alu_cmd_shift_c; -- SLL(I) |
when funct3_slt_c => alu_operation_v := alu_cmd_slt_c; -- SLT(I) |
when funct3_sltu_c => alu_operation_v := alu_cmd_slt_c; -- SLTU(I) |
when funct3_xor_c => alu_operation_v := alu_cmd_xor_c; -- XOR(I) |
when funct3_sr_c => alu_operation_v := alu_cmd_shift_c; -- SRL(I) / SRA(I) |
when funct3_or_c => alu_operation_v := alu_cmd_or_c; -- OR(I) |
when funct3_and_c => alu_operation_v := alu_cmd_and_c; -- AND(I) |
when others => alu_operation_v := (others => '0'); -- undefined |
end case; |
|
-- is rs1 = r0? -- |
rs1_is_r0_v := '0'; |
if (execute_engine.i_reg(instr_rs1_msb_c downto instr_rs1_lsb_c) = "00000") then |
661,20 → 641,35
-- state machine -- |
case execute_engine.state is |
|
when SYS_WAIT => -- Delay cycle (used to wait for side effects to kick in) |
when SYS_WAIT => -- System delay cycle (used to wait for side effects to kick in) ((and to init r0 with zero if it is a physical register)) |
-- ------------------------------------------------------------ |
if (rf_r0_is_reg_c = true) then -- is r0 implemented as physical register, which has to be set to zero? |
-- set reg_file.r0 to zero |
ctrl_nxt(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c) <= (others => '0'); -- rd addr = r0 |
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "11"; -- RF input = CSR output (results zero since there is no valid CSR_read request) |
ctrl_nxt(ctrl_rf_r0_we_c) <= '1'; -- allow write access to r0 |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
end if; |
-- |
execute_engine.state_nxt <= DISPATCH; |
|
when DISPATCH => -- Get new command from instruction prefetch buffer (IPB) |
-- ------------------------------------------------------------ |
when DISPATCH => -- Get new command from instruction prefetch buffer (IPB) |
-- ------------------------------------------------------------ |
if (ipb.avail = '1') then -- instruction available? |
ipb.re <= '1'; |
-- |
trap_ctrl.instr_ma <= ipb.rdata(33); -- misaligned instruction fetch address |
trap_ctrl.instr_be <= ipb.rdata(34); -- bus access fault during instrucion fetch |
illegal_compressed <= ipb.rdata(35); -- invalid decompressed instruction |
-- |
ctrl_nxt(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c) <= ipb.rdata(instr_rd_msb_c downto instr_rd_lsb_c); -- rd addr |
ctrl_nxt(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c) <= ipb.rdata(instr_rs1_msb_c downto instr_rs1_lsb_c); -- rs1 addr |
ctrl_nxt(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c) <= ipb.rdata(instr_rs2_msb_c downto instr_rs2_lsb_c); -- rs2 addr |
-- |
execute_engine.is_ci_nxt <= ipb.rdata(32); -- flag to indicate this is a compressed instruction beeing executed |
execute_engine.i_reg_nxt <= ipb.rdata(31 downto 0); |
execute_engine.if_rst_nxt <= '0'; |
-- |
if (execute_engine.if_rst = '0') then -- if there was no non-linear PC modification |
execute_engine.pc_nxt <= execute_engine.next_pc; |
end if; |
701,47 → 696,64
-- ------------------------------------------------------------ |
case execute_engine.i_reg(instr_opcode_msb_c downto instr_opcode_lsb_c) is |
|
when opcode_alu_c | opcode_alui_c => -- ALU operation |
when opcode_alu_c | opcode_alui_c => -- (immediate) ALU operation |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- use RS1 as ALU.OPA |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= alu_immediate_v; -- use IMM as ALU.OPB for immediate operations |
ctrl_nxt(ctrl_alu_opb_mux_c) <= alu_immediate_v; -- use IMM as ALU.OPB for immediate operations |
ctrl_nxt(ctrl_alu_opc_mux_c) <= not alu_immediate_v; |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_operation_v; -- actual ALU operation |
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "00"; -- RF input = ALU result |
|
-- actual ALU operation (re-coding) -- |
case execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) is |
when funct3_subadd_c => -- ADD(I) / SUB |
if (alu_immediate_v = '0') and (execute_engine.i_reg(instr_funct7_msb_c-1) = '1') then -- not immediate and funct7 => SUB |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_sub_c; -- SUB |
else |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_add_c; -- ADD(I) |
end if; |
when funct3_sll_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_shift_c; -- SLL(I) |
when funct3_slt_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_slt_c; -- SLT(I) |
when funct3_sltu_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_slt_c; -- SLTU(I) |
when funct3_xor_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_xor_c; -- XOR(I) |
when funct3_sr_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_shift_c; -- SRL(I) / SRA(I) |
when funct3_or_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_or_c; -- OR(I) |
when funct3_and_c => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_and_c; -- AND(I) |
when others => ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= (others => '0'); -- undefined |
end case; |
|
-- cp access? -- |
if (CPU_EXTENSION_RISCV_M = true) and (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and |
(execute_engine.i_reg(instr_funct7_lsb_c) = '1') then -- MULDIV? |
ctrl_nxt(ctrl_cp_use_c) <= '1'; -- use CP |
end if; |
|
-- multi cycle alu operation? -- |
if (alu_operation_v = alu_cmd_shift_c) or -- shift operation? |
((CPU_EXTENSION_RISCV_M = true) and (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and |
(execute_engine.i_reg(instr_funct7_lsb_c) = '1')) then -- MULDIV? |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) or -- SLL shift operation? |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) or -- SR shift operation? |
((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and (execute_engine.i_reg(instr_funct7_lsb_c) = '1')) then -- MULDIV? |
execute_engine.state_nxt <= ALU_WAIT; |
else |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
end if; |
-- cp access? -- |
if (CPU_EXTENSION_RISCV_M = true) and (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and |
(execute_engine.i_reg(instr_funct7_lsb_c) = '1') then -- MULDIV? |
ctrl_nxt(ctrl_cp_use_c) <= '1'; -- use CP |
end if; |
|
when opcode_lui_c | opcode_auipc_c => -- load upper immediate (add to PC) |
when opcode_lui_c | opcode_auipc_c => -- load upper immediate / add upper immediate to PC |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_rf_clear_rs1_c) <= '1'; -- force RS1 = r0 (only relevant for LUI) |
if (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_auipc_c(5)) then -- AUIPC |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '1'; -- use PC as ALU.OPA |
else -- LUI |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- use RS1 as ALU.OPA ( = 0) |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '1'; -- ALU.OPA = PC (for AUIPC only) |
if (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_lui_c(5)) then -- LUI |
ctrl_nxt(ctrl_alu_opa_mux_msb_c) <= '1'; -- ALU.OPA = CSR = 0 (hacky: csr.result is 0 since there was no csr_read_request) |
end if; |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_add_c; -- actual ALU operation |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_add_c; -- actual ALU operation = ADD |
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "00"; -- RF input = ALU result |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
execute_engine.state_nxt <= DISPATCH; |
|
when opcode_load_c | opcode_store_c => -- load/store |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- use RS1 as ALU.OPA |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_add_c; -- actual ALU operation |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_add_c; -- actual ALU operation = ADD |
ctrl_nxt(ctrl_bus_mar_we_c) <= '1'; -- write to MAR |
ctrl_nxt(ctrl_bus_mdo_we_c) <= '1'; -- write to MDO (only relevant for stores) |
execute_engine.state_nxt <= LOADSTORE_0; |
749,7 → 761,7
when opcode_branch_c => -- branch instruction |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '1'; -- use PC as ALU.OPA |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_opc_mux_c) <= '1'; -- use RS2 as ALU.OPC |
execute_engine.state_nxt <= BRANCH; |
|
761,7 → 773,7
else -- JALR |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- use RS1 as ALU.OPA |
end if; |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB |
-- save return address -- |
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "10"; -- RF input = next PC (save return address) |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
785,6 → 797,9
when opcode_syscsr_c => -- system/csr access |
-- ------------------------------------------------------------ |
csr.re_nxt <= csr_acc_valid; -- always read CSR if valid access |
ctrl_nxt(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c) <= (others => '0'); -- set rs1_addr to r0 (zero) |
ctrl_nxt(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c) <= ctrl(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c); -- copy rs1_addr to rs2_addr (for CSR mod) |
-- |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_env_c) then -- system |
case execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) is |
when funct12_ecall_c => -- ECALL |
792,12 → 807,12
when funct12_ebreak_c => -- EBREAK |
trap_ctrl.break_point <= '1'; |
when funct12_mret_c => -- MRET |
trap_ctrl.env_end <= '1'; |
execute_engine.pc_nxt <= csr.mepc; |
fetch_engine.reset <= '1'; |
trap_ctrl.env_end <= '1'; |
execute_engine.pc_nxt <= csr.mepc; |
fetch_engine.reset <= '1'; |
execute_engine.if_rst_nxt <= '1'; -- this is a non-linear PC modification |
when funct12_wfi_c => -- WFI = "CPU sleep" |
execute_engine.sleep_nxt <= '1'; -- good night |
when funct12_wfi_c => -- WFI (CPU sleep) |
execute_engine.sleep_nxt <= '1'; -- sleep well |
when others => -- undefined |
NULL; |
end case; |
814,53 → 829,25
|
when CSR_ACCESS => -- write CSR data to RF, write ALU.res to CSR |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_msb_c) <= '0'; -- default |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- default |
ctrl_nxt(ctrl_alu_opb_mux_msb_c) <= '0'; -- default |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '0'; -- default |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_or_c; -- default ALU operation = OR |
ctrl_nxt(ctrl_alu_opb_mux_c) <= execute_engine.i_reg(instr_funct3_msb_c); -- OPB = rs2 (which is rs1 here) / immediate |
case execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) is |
-- register operations -- |
when funct3_csrrw_c => -- CSRRW |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- OPA = rs1 |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- OPB = rs1 |
ctrl_nxt(ctrl_alu_opb_mux_msb_c) <= '1'; -- OPB = rs1 |
when funct3_csrrw_c | funct3_csrrwi_c => -- CSRRW(I) |
ctrl_nxt(ctrl_alu_opa_mux_msb_c downto ctrl_alu_opa_mux_lsb_c) <= "00"; -- OPA = rs1 (which is zero here) |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_or_c; -- actual ALU operation = OR |
csr.we_nxt <= csr_acc_valid; -- always write CSR if valid access |
when funct3_csrrs_c => -- CSRRS |
ctrl_nxt(ctrl_alu_opa_mux_msb_c) <= '1'; -- OPA = csr |
ctrl_nxt(ctrl_alu_opb_mux_msb_c) <= '1'; -- OPB = rs1 |
when funct3_csrrs_c | funct3_csrrsi_c => -- CSRRS(I) |
ctrl_nxt(ctrl_alu_opa_mux_msb_c downto ctrl_alu_opa_mux_lsb_c) <= "10"; -- OPA = CSR |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_or_c; -- actual ALU operation = OR |
csr.we_nxt <= (not rs1_is_r0_v) and csr_acc_valid; -- write CSR if rs1 is not zero_reg and if valid access |
when funct3_csrrc_c => -- CSRRC |
ctrl_nxt(ctrl_alu_opa_mux_msb_c) <= '1'; -- OPA = csr |
ctrl_nxt(ctrl_alu_opb_mux_msb_c) <= '1'; -- OPB = rs1 |
when others => -- CSRRC(I) |
ctrl_nxt(ctrl_alu_opa_mux_msb_c downto ctrl_alu_opa_mux_lsb_c) <= "10"; -- OPA = CSR |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_bclr_c; -- actual ALU operation = bit clear |
csr.we_nxt <= (not rs1_is_r0_v) and csr_acc_valid; -- write CSR if rs1 is not zero_reg and if valid access |
-- immediate operations -- |
when funct3_csrrwi_c => -- CSRRWI |
ctrl_nxt(ctrl_alu_opa_mux_lsb_c) <= '0'; -- OPA = rs1 |
ctrl_nxt(ctrl_rf_clear_rs1_c) <= '1'; -- rs1 = 0 |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- OPB = immediate |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_or_c; -- actual ALU operation = OR |
csr.we_nxt <= csr_acc_valid; -- always write CSR if valid access |
when funct3_csrrsi_c => -- CSRRSI |
ctrl_nxt(ctrl_alu_opa_mux_msb_c) <= '1'; -- OPA = csr |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- OPB = immediate |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_or_c; -- actual ALU operation = OR |
csr.we_nxt <= (not rs1_is_r0_v) and csr_acc_valid; -- write CSR if UIMM5 is not zero (bits from rs1 filed) and if valid access |
when funct3_csrrci_c => -- CSRRCI |
ctrl_nxt(ctrl_alu_opa_mux_msb_c) <= '1'; -- OPA = csr |
ctrl_nxt(ctrl_alu_opb_mux_lsb_c) <= '1'; -- OPB = immediate |
ctrl_nxt(ctrl_alu_cmd2_c downto ctrl_alu_cmd0_c) <= alu_cmd_bclr_c; -- actual ALU operation = bit clear |
csr.we_nxt <= (not rs1_is_r0_v) and csr_acc_valid; -- write CSR if UIMM5 is not zero (bits from rs1 filed) and if valid access |
when others => -- undefined |
NULL; |
end case; |
-- RF write back -- |
ctrl_nxt(ctrl_rf_in_mux_msb_c downto ctrl_rf_in_mux_lsb_c) <= "11"; -- RF input = CSR output |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; -- FIXME should be SYS_WAIT? have another cycle to let side-effects kick in |
execute_engine.state_nxt <= DISPATCH; -- FIXME? should be SYS_WAIT? have another cycle to let side-effects kick in |
|
when ALU_WAIT => -- wait for multi-cycle ALU operation (shifter or CP) to finish |
-- ------------------------------------------------------------ |
929,7 → 916,7
|
-- Illegal CSR Access Check --------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
invalid_csr_access_check: process(execute_engine, csr) |
invalid_csr_access_check: process(execute_engine, csr.privilege) |
variable is_m_mode_v : std_ulogic; |
begin |
-- are we in machine mode? -- |
/rtl/core/neorv32_cpu_regfile.vhd
2,7 → 2,9
-- # << NEORV32 - CPU Register File >> # |
-- # ********************************************************************************************* # |
-- # General purpose data registers. 32 entries for normal mode, 16 entries for embedded mode when # |
-- # RISC-V M extension is enabled. x0 output is allways set to zero. # |
-- # RISC-V "E" extension is enabled. Register zero (r0/x0) is a normal physical registers, that # |
-- # has to be initialized to zero by the CPU control system. For normal operations, x0 cannot be # |
-- # written. # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
70,13 → 72,8
signal reg_file_emb : reg_file_emb_t; |
signal rf_write_data : std_ulogic_vector(data_width_c-1 downto 0); -- actual write-back data |
signal valid_wr : std_ulogic; -- writing not to r0 |
signal rs1_read : std_ulogic_vector(data_width_c-1 downto 0); -- internal operand rs1 |
signal rs2_read : std_ulogic_vector(data_width_c-1 downto 0); -- internal operand rs2 |
|
-- reading from r0? -- |
signal rs1_clear, rs2_clear : std_ulogic; |
|
|
-- attributes - these are *NOT mandatory*; just for footprint / timing optimization -- |
-- -------------------------------------------------------------------------------- -- |
|
104,9 → 101,9
end case; |
end process input_mux; |
|
-- only write if destination is not x0 (pretty irrelevant, but might save some power) -- |
valid_wr <= or_all_f(ctrl_i(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c)) when (CPU_EXTENSION_RISCV_E = false) else |
or_all_f(ctrl_i(ctrl_rf_rd_adr3_c downto ctrl_rf_rd_adr0_c)); |
-- only write if destination is not x0; except we are forcing a r0 write access -- |
valid_wr <= or_all_f(ctrl_i(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c)) or ctrl_i(ctrl_rf_r0_we_c) when (CPU_EXTENSION_RISCV_E = false) else |
or_all_f(ctrl_i(ctrl_rf_rd_adr3_c downto ctrl_rf_rd_adr0_c)) or ctrl_i(ctrl_rf_r0_we_c); |
|
|
-- Register file read/write access -------------------------------------------------------- |
115,49 → 112,25
begin |
if rising_edge(clk_i) then -- sync read and write |
if (CPU_EXTENSION_RISCV_E = false) then -- normal register file with 32 entries |
-- check if reading from r0 -- |
rs1_clear <= '0'; |
rs2_clear <= '0'; |
if (ctrl_i(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c) = "00000") then |
rs1_clear <= '1'; |
end if; |
if (ctrl_i(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c) = "00000") then |
rs2_clear <= '1'; |
end if; |
-- write -- |
if (ctrl_i(ctrl_rf_wb_en_c) = '1') and (valid_wr = '1') then -- valid write-back |
if (ctrl_i(ctrl_rf_wb_en_c) = '1') and ((valid_wr = '1') or (rf_r0_is_reg_c = false)) then -- valid write-back |
reg_file(to_integer(unsigned(ctrl_i(ctrl_rf_rd_adr4_c downto ctrl_rf_rd_adr0_c)))) <= rf_write_data; |
end if; |
-- read -- |
rs1_read <= reg_file(to_integer(unsigned(ctrl_i(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c)))); |
rs2_read <= reg_file(to_integer(unsigned(ctrl_i(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c)))); |
rs1_o <= reg_file(to_integer(unsigned(ctrl_i(ctrl_rf_rs1_adr4_c downto ctrl_rf_rs1_adr0_c)))); |
rs2_o <= reg_file(to_integer(unsigned(ctrl_i(ctrl_rf_rs2_adr4_c downto ctrl_rf_rs2_adr0_c)))); |
|
else -- embedded register file with 16 entries |
-- check if reading from r0 -- |
rs1_clear <= '0'; |
rs2_clear <= '0'; |
if (ctrl_i(ctrl_rf_rs1_adr3_c downto ctrl_rf_rs1_adr0_c) = "0000") then |
rs1_clear <= '1'; |
end if; |
if (ctrl_i(ctrl_rf_rs2_adr3_c downto ctrl_rf_rs2_adr0_c) = "0000") then |
rs2_clear <= '1'; |
end if; |
-- write -- |
if (ctrl_i(ctrl_rf_wb_en_c) = '1') and (valid_wr = '1') then -- valid write-back |
if (ctrl_i(ctrl_rf_wb_en_c) = '1') and ((valid_wr = '1') or (rf_r0_is_reg_c = false)) then -- valid write-back |
reg_file_emb(to_integer(unsigned(ctrl_i(ctrl_rf_rd_adr3_c downto ctrl_rf_rd_adr0_c)))) <= rf_write_data; |
end if; |
-- read -- |
rs1_read <= reg_file_emb(to_integer(unsigned(ctrl_i(ctrl_rf_rs1_adr3_c downto ctrl_rf_rs1_adr0_c)))); |
rs2_read <= reg_file_emb(to_integer(unsigned(ctrl_i(ctrl_rf_rs2_adr3_c downto ctrl_rf_rs2_adr0_c)))); |
rs1_o <= reg_file_emb(to_integer(unsigned(ctrl_i(ctrl_rf_rs1_adr3_c downto ctrl_rf_rs1_adr0_c)))); |
rs2_o <= reg_file_emb(to_integer(unsigned(ctrl_i(ctrl_rf_rs2_adr3_c downto ctrl_rf_rs2_adr0_c)))); |
end if; |
end if; |
end process rf_access; |
|
|
-- Check if reading from x0 --------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
rs1_o <= (others => '0') when ((rs1_clear or ctrl_i(ctrl_rf_clear_rs1_c)) = '1') else rs1_read; |
rs2_o <= (others => '0') when (rs2_clear = '1') else rs2_read; |
|
|
end neorv32_cpu_regfile_rtl; |
/rtl/core/neorv32_mtime.vhd
103,16 → 103,16
wr_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
-- mtimecmp -- |
if (wren = '1') then |
if (addr = mtime_cmp_lo_addr_c) then -- low |
mtimecmp_lo <= data_i; |
end if; |
if (addr = mtime_cmp_hi_addr_c) then -- high |
mtimecmp_hi <= data_i; |
end if; |
-- mtimecmp low -- |
if (wren = '1') and (addr = mtime_cmp_lo_addr_c) then |
mtimecmp_lo <= data_i; |
end if; |
|
-- mtimecmp high -- |
if (wren = '1') and (addr = mtime_cmp_hi_addr_c) then |
mtimecmp_hi <= data_i; |
end if; |
|
-- mtime low -- |
if (wren = '1') and (addr = mtime_time_lo_addr_c) then |
mtime_lo_msb_ff <= '0'; |
140,15 → 140,18
ack_o <= acc_en and (rden_i or wren_i); |
data_o <= (others => '0'); -- default |
if (rden_i = '1') and (acc_en = '1') then |
if (addr = mtime_time_lo_addr_c) then -- mtime LOW |
data_o <= mtime_lo(31 downto 00); |
elsif (addr = mtime_time_hi_addr_c) then -- mtime HIGH |
data_o <= mtime_hi; |
elsif (addr = mtime_cmp_lo_addr_c) then -- mtimecmp LOW |
data_o <= mtimecmp_lo; |
else -- (addr = mtime_cmp_hi_addr_c) then -- mtimecmp HIGH |
data_o <= mtimecmp_hi; |
end if; |
case addr is |
when mtime_time_lo_addr_c => -- mtime LOW |
data_o <= mtime_lo(31 downto 00); |
when mtime_time_hi_addr_c => -- mtime HIGH |
data_o <= mtime_hi; |
when mtime_cmp_lo_addr_c => -- mtimecmp LOW |
data_o <= mtimecmp_lo; |
when mtime_cmp_hi_addr_c => -- mtimecmp HIGH |
data_o <= mtimecmp_hi; |
when others => |
data_o <= (others => '0'); |
end case; |
end if; |
end if; |
end process rd_access; |
/rtl/core/neorv32_package.vhd
40,10 → 40,11
|
-- Architecture Constants/Configuration --------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
constant data_width_c : natural := 32; -- data width - FIXED! |
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01040300"; -- no touchy! |
constant pmp_max_r_c : natural := 8; -- max PMP regions - FIXED! |
constant ipb_entries_c : natural := 2; -- entries in instruction prefetch buffer, must be a power of 2, default=2 |
constant data_width_c : natural := 32; -- data width - FIXED! |
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01040302"; -- no touchy! |
constant pmp_max_r_c : natural := 8; -- max PMP regions - FIXED! |
constant ipb_entries_c : natural := 2; -- entries in instruction prefetch buffer, must be a power of 2, default=2 |
constant rf_r0_is_reg_c : boolean := true; -- reg_file.r0 is a physical register that has to be initialized to zero |
|
-- Helper Functions ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
177,7 → 178,7
constant ctrl_rf_rd_adr3_c : natural := 15; -- destiantion register address bit 3 |
constant ctrl_rf_rd_adr4_c : natural := 16; -- destiantion register address bit 4 |
constant ctrl_rf_wb_en_c : natural := 17; -- write back enable |
constant ctrl_rf_clear_rs1_c : natural := 18; -- force rs1=r0 |
constant ctrl_rf_r0_we_c : natural := 18; -- allow write access to r0 (zero), also forces dst=r0 |
-- alu -- |
constant ctrl_alu_cmd0_c : natural := 19; -- ALU command bit 0 |
constant ctrl_alu_cmd1_c : natural := 20; -- ALU command bit 1 |
184,35 → 185,34
constant ctrl_alu_cmd2_c : natural := 21; -- ALU command bit 2 |
constant ctrl_alu_opa_mux_lsb_c : natural := 22; -- operand A select lsb (00=rs1, 01=PC) |
constant ctrl_alu_opa_mux_msb_c : natural := 23; -- operand A select msb (1-=CSR) |
constant ctrl_alu_opb_mux_lsb_c : natural := 24; -- operand B select lsb (00=rs2, 01=IMM) |
constant ctrl_alu_opb_mux_msb_c : natural := 25; -- operand B select msb (1-=rs1) |
constant ctrl_alu_opc_mux_c : natural := 26; -- operand C select (0=IMM, 1=rs2) |
constant ctrl_alu_unsigned_c : natural := 27; -- is unsigned ALU operation |
constant ctrl_alu_shift_dir_c : natural := 28; -- shift direction (0=left, 1=right) |
constant ctrl_alu_shift_ar_c : natural := 29; -- is arithmetic shift |
constant ctrl_alu_opb_mux_c : natural := 24; -- operand B select (0=rs2, 1=IMM) |
constant ctrl_alu_opc_mux_c : natural := 25; -- operand C select (0=IMM, 1=rs2) |
constant ctrl_alu_unsigned_c : natural := 26; -- is unsigned ALU operation |
constant ctrl_alu_shift_dir_c : natural := 27; -- shift direction (0=left, 1=right) |
constant ctrl_alu_shift_ar_c : natural := 28; -- is arithmetic shift |
-- bus interface -- |
constant ctrl_bus_size_lsb_c : natural := 30; -- transfer size lsb (00=byte, 01=half-word) |
constant ctrl_bus_size_msb_c : natural := 31; -- transfer size msb (10=word, 11=?) |
constant ctrl_bus_rd_c : natural := 32; -- read data request |
constant ctrl_bus_wr_c : natural := 33; -- write data request |
constant ctrl_bus_if_c : natural := 34; -- instruction fetch request |
constant ctrl_bus_mar_we_c : natural := 35; -- memory address register write enable |
constant ctrl_bus_mdo_we_c : natural := 36; -- memory data out register write enable |
constant ctrl_bus_mdi_we_c : natural := 37; -- memory data in register write enable |
constant ctrl_bus_unsigned_c : natural := 38; -- is unsigned load |
constant ctrl_bus_ierr_ack_c : natural := 39; -- acknowledge instruction fetch bus exception |
constant ctrl_bus_derr_ack_c : natural := 40; -- acknowledge data access bus exception |
constant ctrl_bus_fence_c : natural := 41; -- executed fence operation |
constant ctrl_bus_fencei_c : natural := 42; -- executed fencei operation |
constant ctrl_bus_size_lsb_c : natural := 29; -- transfer size lsb (00=byte, 01=half-word) |
constant ctrl_bus_size_msb_c : natural := 30; -- transfer size msb (10=word, 11=?) |
constant ctrl_bus_rd_c : natural := 31; -- read data request |
constant ctrl_bus_wr_c : natural := 32; -- write data request |
constant ctrl_bus_if_c : natural := 33; -- instruction fetch request |
constant ctrl_bus_mar_we_c : natural := 34; -- memory address register write enable |
constant ctrl_bus_mdo_we_c : natural := 35; -- memory data out register write enable |
constant ctrl_bus_mdi_we_c : natural := 36; -- memory data in register write enable |
constant ctrl_bus_unsigned_c : natural := 37; -- is unsigned load |
constant ctrl_bus_ierr_ack_c : natural := 38; -- acknowledge instruction fetch bus exception |
constant ctrl_bus_derr_ack_c : natural := 39; -- acknowledge data access bus exception |
constant ctrl_bus_fence_c : natural := 40; -- executed fence operation |
constant ctrl_bus_fencei_c : natural := 41; -- executed fencei operation |
-- co-processor -- |
constant ctrl_cp_use_c : natural := 43; -- is cp operation |
constant ctrl_cp_id_lsb_c : natural := 44; -- cp select lsb |
constant ctrl_cp_id_msb_c : natural := 45; -- cp select msb |
constant ctrl_cp_cmd0_c : natural := 46; -- cp command bit 0 |
constant ctrl_cp_cmd1_c : natural := 47; -- cp command bit 1 |
constant ctrl_cp_cmd2_c : natural := 48; -- cp command bit 2 |
constant ctrl_cp_use_c : natural := 42; -- is cp operation |
constant ctrl_cp_id_lsb_c : natural := 43; -- cp select ID lsb |
constant ctrl_cp_id_msb_c : natural := 44; -- cp select ID msb |
constant ctrl_cp_cmd0_c : natural := 45; -- cp command bit 0 |
constant ctrl_cp_cmd1_c : natural := 46; -- cp command bit 1 |
constant ctrl_cp_cmd2_c : natural := 47; -- cp command bit 2 |
-- control bus size -- |
constant ctrl_width_c : natural := 49; -- control bus size |
constant ctrl_width_c : natural := 48; -- control bus size |
|
-- ALU Comparator Bus --------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
331,7 → 331,7
constant alu_cmd_xor_c : std_ulogic_vector(2 downto 0) := "100"; -- r <= A xor B |
constant alu_cmd_or_c : std_ulogic_vector(2 downto 0) := "101"; -- r <= A or B |
constant alu_cmd_and_c : std_ulogic_vector(2 downto 0) := "110"; -- r <= A and B |
constant alu_cmd_bclr_c : std_ulogic_vector(2 downto 0) := "111"; -- r <= A and (not B) |
constant alu_cmd_bclr_c : std_ulogic_vector(2 downto 0) := "111"; -- r <= A and (not B) |
|
-- Trap ID Codes -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
/rtl/core/neorv32_top.vhd
302,7 → 302,7
generic map ( |
-- General -- |
HW_THREAD_ID => (others => '0'), -- hardware thread id |
CPU_BOOT_ADDR => cpu_boot_addr_c, -- cpu boot address |
CPU_BOOT_ADDR => cpu_boot_addr_c, -- cpu boot address |
-- RISC-V CPU Extensions -- |
CPU_EXTENSION_RISCV_C => CPU_EXTENSION_RISCV_C, -- implement compressed extension? |
CPU_EXTENSION_RISCV_E => CPU_EXTENSION_RISCV_E, -- implement embedded RF extension? |
311,13 → 311,13
CPU_EXTENSION_RISCV_Zicsr => CPU_EXTENSION_RISCV_Zicsr, -- implement CSR system? |
CPU_EXTENSION_RISCV_Zifencei => CPU_EXTENSION_RISCV_Zifencei, -- implement instruction stream sync.? |
-- Extension Options -- |
FAST_MUL_EN => FAST_MUL_EN, -- use DSPs for M extension's multiplier |
FAST_MUL_EN => FAST_MUL_EN, -- use DSPs for M extension's multiplier |
-- Physical Memory Protection (PMP) -- |
PMP_USE => PMP_USE, -- implement PMP? |
PMP_NUM_REGIONS => PMP_NUM_REGIONS, -- number of regions (max 8) |
PMP_GRANULARITY => PMP_GRANULARITY, -- minimal region granularity (1=8B, 2=16B, 3=32B, ...) default is 64k |
-- Bus Interface -- |
BUS_TIMEOUT => MEM_EXT_TIMEOUT -- cycles after which a valid bus access will timeout |
BUS_TIMEOUT => MEM_EXT_TIMEOUT -- cycles after which a valid bus access will timeout |
) |
port map ( |
-- global control -- |
/rtl/top_templates/README.md
1,7 → 1,7
## Top Templates |
|
The top entity of the NEORV32 processor is `rtl/core/neorv32_top.vhd`. This folder provides additional |
top entities, that instantiate the processor's top entity and have a different top interface. |
top entities/wrappers that instantiate the processor's top entity to provide a different interface. |
|
### `neorv32_test_setup.vhd` |
|
8,3 → 8,8
This entity is intended as "FPGA hello world" example for playing with the NEORV32. It uses only some of the |
provided peripherals and provides a very simple and basic interface - only the clock, reset, UART and a subset |
of the GPIO output port are propagated to the outer world. |
|
### `neorv32_top_stdlogic.vhd` |
|
Same entity (generics and interface ports) as the default NEORV32 Processor top entity (`rtl/core/neorv32_top.vhd`), |
but with _resolved_ port signals: All ports are of type `std_logic` or `std_logic_vector`, respectively. |
/rtl/top_templates/neorv32_top_stdlogic.vhd
0,0 → 1,278
-- ################################################################################################# |
-- # << NEORV32 - Processor Top Entity with Resolved Port Signals (std_logic/std_logic_vector) >> # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
-- # Copyright (c) 2020, Stephan Nolting. All rights reserved. # |
-- # # |
-- # Redistribution and use in source and binary forms, with or without modification, are # |
-- # permitted provided that the following conditions are met: # |
-- # # |
-- # 1. Redistributions of source code must retain the above copyright notice, this list of # |
-- # conditions and the following disclaimer. # |
-- # # |
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
-- # conditions and the following disclaimer in the documentation and/or other materials # |
-- # provided with the distribution. # |
-- # # |
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
-- # endorse or promote products derived from this software without specific prior written # |
-- # permission. # |
-- # # |
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
library neorv32; |
use neorv32.neorv32_package.all; |
|
entity neorv32_top_stdlogic is |
generic ( |
-- General -- |
CLOCK_FREQUENCY : natural := 0; -- clock frequency of clk_i in Hz |
BOOTLOADER_USE : boolean := true; -- implement processor-internal bootloader? |
USER_CODE : std_logic_vector(31 downto 0) := x"00000000"; -- custom user code |
-- RISC-V CPU Extensions -- |
CPU_EXTENSION_RISCV_C : boolean := false; -- implement compressed extension? |
CPU_EXTENSION_RISCV_E : boolean := false; -- implement embedded RF extension? |
CPU_EXTENSION_RISCV_M : boolean := false; -- implement muld/div extension? |
CPU_EXTENSION_RISCV_U : boolean := false; -- implement user mode extension? |
CPU_EXTENSION_RISCV_Zicsr : boolean := true; -- implement CSR system? |
CPU_EXTENSION_RISCV_Zifencei : boolean := true; -- implement instruction stream sync.? |
-- Extension Options -- |
FAST_MUL_EN : boolean := false; -- use DSPs for M extension's multiplier |
-- Physical Memory Protection (PMP) -- |
PMP_USE : boolean := false; -- implement PMP? |
PMP_NUM_REGIONS : natural := 4; -- number of regions (max 8) |
PMP_GRANULARITY : natural := 14; -- minimal region granularity (1=8B, 2=16B, 3=32B, ...) default is 64k |
-- Internal Instruction memory -- |
MEM_INT_IMEM_USE : boolean := true; -- implement processor-internal instruction memory |
MEM_INT_IMEM_SIZE : natural := 16*1024; -- size of processor-internal instruction memory in bytes |
MEM_INT_IMEM_ROM : boolean := false; -- implement processor-internal instruction memory as ROM |
-- Internal Data memory -- |
MEM_INT_DMEM_USE : boolean := true; -- implement processor-internal data memory |
MEM_INT_DMEM_SIZE : natural := 8*1024; -- size of processor-internal data memory in bytes |
-- External memory interface -- |
MEM_EXT_USE : boolean := false; -- implement external memory bus interface? |
MEM_EXT_REG_STAGES : natural := 2; -- number of interface register stages (0,1,2) |
MEM_EXT_TIMEOUT : natural := 15; -- cycles after which a valid bus access will timeout |
-- Processor peripherals -- |
IO_GPIO_USE : boolean := true; -- implement general purpose input/output port unit (GPIO)? |
IO_MTIME_USE : boolean := true; -- implement machine system timer (MTIME)? |
IO_UART_USE : boolean := true; -- implement universal asynchronous receiver/transmitter (UART)? |
IO_SPI_USE : boolean := true; -- implement serial peripheral interface (SPI)? |
IO_TWI_USE : boolean := true; -- implement two-wire interface (TWI)? |
IO_PWM_USE : boolean := true; -- implement pulse-width modulation unit (PWM)? |
IO_WDT_USE : boolean := true; -- implement watch dog timer (WDT)? |
IO_TRNG_USE : boolean := false; -- implement true random number generator (TRNG)? |
IO_DEVNULL_USE : boolean := true; -- implement dummy device (DEVNULL)? |
IO_CFU_USE : boolean := false -- implement custom functions unit (CFU)? |
); |
port ( |
-- Global control -- |
clk_i : in std_logic := '0'; -- global clock, rising edge |
rstn_i : in std_logic := '0'; -- global reset, low-active, async |
-- Wishbone bus interface (available if MEM_EXT_USE = true) -- |
wb_adr_o : out std_logic_vector(31 downto 0); -- address |
wb_dat_i : in std_logic_vector(31 downto 0) := (others => '0'); -- read data |
wb_dat_o : out std_logic_vector(31 downto 0); -- write data |
wb_we_o : out std_logic; -- read/write |
wb_sel_o : out std_logic_vector(03 downto 0); -- byte enable |
wb_stb_o : out std_logic; -- strobe |
wb_cyc_o : out std_logic; -- valid cycle |
wb_ack_i : in std_logic := '0'; -- transfer acknowledge |
wb_err_i : in std_logic := '0'; -- transfer error |
-- Advanced memory control signals (available if MEM_EXT_USE = true) -- |
fence_o : out std_logic; -- indicates an executed FENCE operation |
fencei_o : out std_logic; -- indicates an executed FENCEI operation |
-- GPIO (available if IO_GPIO_USE = true) -- |
gpio_o : out std_logic_vector(31 downto 0); -- parallel output |
gpio_i : in std_logic_vector(31 downto 0) := (others => '0'); -- parallel input |
-- UART (available if IO_UART_USE = true) -- |
uart_txd_o : out std_logic; -- UART send data |
uart_rxd_i : in std_logic := '0'; -- UART receive data |
-- SPI (available if IO_SPI_USE = true) -- |
spi_sck_o : out std_logic; -- SPI serial clock |
spi_sdo_o : out std_logic; -- controller data out, peripheral data in |
spi_sdi_i : in std_logic := '0'; -- controller data in, peripheral data out |
spi_csn_o : out std_logic_vector(07 downto 0); -- SPI CS |
-- TWI (available if IO_TWI_USE = true) -- |
twi_sda_io : inout std_logic := 'H'; -- twi serial data line |
twi_scl_io : inout std_logic := 'H'; -- twi serial clock line |
-- PWM (available if IO_PWM_USE = true) -- |
pwm_o : out std_logic_vector(03 downto 0); -- pwm channels |
-- Interrupts -- |
msw_irq_i : in std_logic := '0'; -- machine software interrupt |
mext_irq_i : in std_logic := '0' -- machine external interrupt |
); |
end neorv32_top_stdlogic; |
|
architecture neorv32_top_stdlogic_rtl of neorv32_top_stdlogic is |
|
-- type conversion -- |
constant USER_CODE_INT : std_ulogic_vector(31 downto 0) := std_ulogic_vector(USER_CODE); |
-- |
signal clk_i_int : std_ulogic; |
signal rstn_i_int : std_ulogic; |
-- |
signal wb_adr_o_int : std_ulogic_vector(31 downto 0); |
signal wb_dat_i_int : std_ulogic_vector(31 downto 0); |
signal wb_dat_o_int : std_ulogic_vector(31 downto 0); |
signal wb_we_o_int : std_ulogic; |
signal wb_sel_o_int : std_ulogic_vector(03 downto 0); |
signal wb_stb_o_int : std_ulogic; |
signal wb_cyc_o_int : std_ulogic; |
signal wb_ack_i_int : std_ulogic; |
signal wb_err_i_int : std_ulogic; |
-- |
signal fence_o_int : std_ulogic; |
signal fencei_o_int : std_ulogic; |
-- |
signal gpio_o_int : std_ulogic_vector(31 downto 0); |
signal gpio_i_int : std_ulogic_vector(31 downto 0); |
-- |
signal uart_txd_o_int : std_ulogic; |
signal uart_rxd_i_int : std_ulogic; |
-- |
signal spi_sck_o_int : std_ulogic; |
signal spi_sdo_o_int : std_ulogic; |
signal spi_sdi_i_int : std_ulogic; |
signal spi_csn_o_int : std_ulogic_vector(07 downto 0); |
-- |
signal pwm_o_int : std_ulogic_vector(03 downto 0); |
-- |
signal msw_irq_i_int : std_ulogic; |
signal mext_irq_i_int : std_ulogic; |
|
begin |
|
-- The Core Of The Problem ---------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_top_inst: neorv32_top |
generic map ( |
-- General -- |
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz |
BOOTLOADER_USE => BOOTLOADER_USE, -- implement processor-internal bootloader? |
USER_CODE => USER_CODE_INT, -- custom user code |
-- RISC-V CPU Extensions -- |
CPU_EXTENSION_RISCV_C => CPU_EXTENSION_RISCV_C, -- implement compressed extension? |
CPU_EXTENSION_RISCV_E => CPU_EXTENSION_RISCV_E, -- implement embedded RF extension? |
CPU_EXTENSION_RISCV_M => CPU_EXTENSION_RISCV_M, -- implement muld/div extension? |
CPU_EXTENSION_RISCV_U => CPU_EXTENSION_RISCV_U, -- implement user mode extension? |
CPU_EXTENSION_RISCV_Zicsr => CPU_EXTENSION_RISCV_Zicsr, -- implement CSR system? |
CPU_EXTENSION_RISCV_Zifencei => CPU_EXTENSION_RISCV_Zifencei, -- implement instruction stream sync.? |
-- Extension Options -- |
FAST_MUL_EN => FAST_MUL_EN, -- use DSPs for M extension's multiplier |
-- Physical Memory Protection (PMP) -- |
PMP_USE => PMP_USE, -- implement PMP? |
PMP_NUM_REGIONS => PMP_NUM_REGIONS, -- number of regions (max 16) |
PMP_GRANULARITY => PMP_GRANULARITY, -- region granularity (1=8B, 2=16B, 3=32B, ...) default is 64k |
-- Internal Instruction memory -- |
MEM_INT_IMEM_USE => MEM_INT_IMEM_USE, -- implement processor-internal instruction memory |
MEM_INT_IMEM_SIZE => MEM_INT_IMEM_SIZE, -- size of processor-internal instruction memory in bytes |
MEM_INT_IMEM_ROM => MEM_INT_IMEM_ROM, -- implement processor-internal instruction memory as ROM |
-- Internal Data memory -- |
MEM_INT_DMEM_USE => MEM_INT_DMEM_USE, -- implement processor-internal data memory |
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE, -- size of processor-internal data memory in bytes |
-- External memory interface -- |
MEM_EXT_USE => MEM_EXT_USE, -- implement external memory bus interface? |
MEM_EXT_REG_STAGES => MEM_EXT_REG_STAGES, -- number of interface register stages (0,1,2) |
MEM_EXT_TIMEOUT => MEM_EXT_TIMEOUT, -- cycles after which a valid bus access will timeout |
-- Processor peripherals -- |
IO_GPIO_USE => IO_GPIO_USE, -- implement general purpose input/output port unit (GPIO)? |
IO_MTIME_USE => IO_MTIME_USE, -- implement machine system timer (MTIME)? |
IO_UART_USE => IO_UART_USE, -- implement universal asynchronous receiver/transmitter (UART)? |
IO_SPI_USE => IO_SPI_USE, -- implement serial peripheral interface (SPI)? |
IO_TWI_USE => IO_TWI_USE, -- implement two-wire interface (TWI)? |
IO_PWM_USE => IO_PWM_USE, -- implement pulse-width modulation unit (PWM)? |
IO_WDT_USE => IO_WDT_USE, -- implement watch dog timer (WDT)? |
IO_TRNG_USE => IO_TRNG_USE, -- implement true random number generator (TRNG)? |
IO_DEVNULL_USE => IO_DEVNULL_USE, -- implement dummy device (DEVNULL)? |
IO_CFU_USE => IO_CFU_USE -- implement custom functions unit (CFU)? |
) |
port map ( |
-- Global control -- |
clk_i => clk_i_int, -- global clock, rising edge |
rstn_i => rstn_i_int, -- global reset, low-active, async |
-- Wishbone bus interface -- |
wb_adr_o => wb_adr_o_int, -- address |
wb_dat_i => wb_dat_i_int, -- read data |
wb_dat_o => wb_dat_o_int, -- write data |
wb_we_o => wb_we_o_int, -- read/write |
wb_sel_o => wb_sel_o_int, -- byte enable |
wb_stb_o => wb_stb_o_int, -- strobe |
wb_cyc_o => wb_cyc_o_int, -- valid cycle |
wb_ack_i => wb_ack_i_int, -- transfer acknowledge |
wb_err_i => wb_err_i_int, -- transfer error |
-- Advanced memory control signals -- |
fence_o => fence_o_int, -- indicates an executed FENCE operation |
fencei_o => fencei_o_int, -- indicates an executed FENCEI operation |
-- GPIO -- |
gpio_o => gpio_o_int, -- parallel output |
gpio_i => gpio_i_int, -- parallel input |
-- UART -- |
uart_txd_o => uart_txd_o_int, -- UART send data |
uart_rxd_i => uart_rxd_i_int, -- UART receive data |
-- SPI -- |
spi_sck_o => spi_sck_o_int, -- SPI serial clock |
spi_sdo_o => spi_sdo_o_int, -- controller data out, peripheral data in |
spi_sdi_i => spi_sdi_i_int, -- controller data in, peripheral data out |
spi_csn_o => spi_csn_o_int, -- SPI CS |
-- TWI -- |
twi_sda_io => twi_sda_io, -- twi serial data line |
twi_scl_io => twi_scl_io, -- twi serial clock line |
-- PWM -- |
pwm_o => pwm_o_int, -- pwm channels |
-- Interrupts -- |
msw_irq_i => msw_irq_i_int, -- machine software interrupt |
mext_irq_i => mext_irq_i_int -- machine external interrupt |
); |
|
-- type conversion -- |
clk_i_int <= std_ulogic(clk_i); |
rstn_i_int <= std_ulogic(rstn_i); |
|
wb_adr_o <= std_logic_vector(wb_adr_o_int); |
wb_dat_i_int <= std_ulogic_vector(wb_dat_i); |
wb_dat_o <= std_logic_vector(wb_dat_o_int); |
wb_we_o <= std_logic(wb_we_o_int); |
wb_sel_o <= std_logic_vector(wb_sel_o_int); |
wb_stb_o <= std_logic(wb_stb_o_int); |
wb_cyc_o <= std_logic(wb_cyc_o_int); |
wb_ack_i_int <= std_ulogic(wb_ack_i); |
wb_err_i_int <= std_ulogic(wb_err_i); |
|
fence_o <= std_logic(fence_o_int); |
fencei_o <= std_logic(fencei_o_int); |
|
gpio_o <= std_logic_vector(gpio_o_int); |
gpio_i_int <= std_ulogic_vector(gpio_i); |
|
uart_txd_o <= std_logic(uart_txd_o_int); |
uart_rxd_i_int <= std_ulogic(uart_rxd_i); |
|
spi_sck_o <= std_logic(spi_sck_o_int); |
spi_sdo_o <= std_logic(spi_sdo_o_int); |
spi_sdi_i_int <= std_ulogic(spi_sdi_i); |
spi_csn_o <= std_logic_vector(spi_csn_o_int); |
|
pwm_o <= std_logic_vector(pwm_o_int); |
|
msw_irq_i_int <= std_ulogic(msw_irq_i); |
mext_irq_i_int <= std_ulogic(mext_irq_i); |
|
|
end neorv32_top_stdlogic_rtl; |
/sim/ghdl/ghdl_sim.sh
13,6 → 13,7
# The directories of the hw source files |
srcdir_core=$homedir/rtl/core |
srcdir_sim=$homedir/sim |
srcdir_top_templates=$homedir/rtl/top_templates |
|
# Show GHDL version |
ghdl -v |
26,6 → 27,7
echo "Simulation source files:" |
ls -l $srcdir_core |
ls -l $srcdir_sim |
ls -l $srcdir_top_templates |
|
# Just a hint |
echo "" |
62,6 → 64,9
ghdl -a --work=neorv32 $srcdir_core/neorv32_wdt.vhd |
ghdl -a --work=neorv32 $srcdir_core/neorv32_wishbone.vhd |
# |
ghdl -a --work=neorv32 $srcdir_top_templates/neorv32_test_setup.vhd |
ghdl -a --work=neorv32 $srcdir_top_templates/neorv32_top_stdlogic.vhd |
# |
ghdl -a --work=neorv32 $srcdir_sim/*.vhd |
|
# Prepare simulation output files |
/README.md
132,7 → 132,7
- Optional universal asynchronous receiver and transmitter (**UART**) |
- Optional 8/16/24/32-bit serial peripheral interface controller (**SPI**) with 8 dedicated chip select lines |
- Optional two wire serial interface controller (**TWI**), compatible to the I²C standard |
- Optional general purpose parallel IO port (**GPIO**), 16xOut & 16xIn, with pin-change interrupt |
- Optional general purpose parallel IO port (**GPIO**), 32xOut & 32xIn, with pin-change interrupt |
- Optional 32-bit external bus interface, Wishbone b4 compliant (**WISHBONE**) |
- Optional watchdog timer (**WDT**) |
- Optional PWM controller with 4 channels and 8-bit duty cycle resolution (**PWM**) |
172,7 → 172,7
* ALU instructions: `C.ADDI4SPN` `C.ADDI` `C.ADD` `C.ADDI16SP` `C.LI` `C.LUI` `C.SLLI` `C.SRLI` `C.SRAI` `C.ANDI` `C.SUB` `C.XOR` `C.OR` `C.AND` `C.MV` `C.NOP` |
* Jump and branch instructions: `C.J` `C.JAL` `C.JR` `C.JALR` `C.BEQZ` `C.BNEZ` |
* Memory instructions: `C.LW` `C.SW` `C.LWSP` `C.SWSP` |
* Misc instructions: `C.EBREAK` (only with `Zicsr` extension) |
* System instructions: `C.EBREAK` (only with `Zicsr` extension) |
|
**Embedded CPU version** (`E` extension): |
* Reduced register file (only the 16 lowest registers) |
255,23 → 255,23
|
Results generated for hardware version: `1.3.6.5` |
|
| Module | Description | LEs | FFs | Memory bits | DSPs | |
|:----------|:------------------------------------------------|:---:|:---:|:-----------:|:----:| |
| BOOT ROM | Bootloader ROM (4kB) | 4 | 1 | 32 768 | 0 | |
| BUSSWITCH | Mux for CPU I & D interfaces | 62 | 8 | 0 | 0 | |
| CFU | Custom functions unit | - | - | - | - | |
| DEVNULL | Dummy device | 3 | 1 | 0 | 0 | |
| DMEM | Processor-internal data memory (8kB) | 12 | 2 | 65 536 | 0 | |
| GPIO | General purpose input/output ports | 40 | 33 | 0 | 0 | |
| IMEM | Processor-internal instruction memory (16kb) | 7 | 2 | 131 072 | 0 | |
| MTIME | Machine system timer | 266 | 166 | 0 | 0 | |
| PWM | Pulse-width modulation controller | 72 | 69 | 0 | 0 | |
| SPI | Serial peripheral interface | 198 | 125 | 0 | 0 | |
| SYSINFO | System configuration information memory | 10 | 9 | 0 | 0 | |
| TRNG | True random number generator | 105 | 93 | 0 | 0 | |
| TWI | Two-wire interface | 75 | 44 | 0 | 0 | |
| UART | Universal asynchronous receiver/transmitter | 153 | 108 | 0 | 0 | |
| WDT | Watchdog timer | 59 | 45 | 0 | 0 | |
| Module | Description | LEs | FFs | Memory bits | DSPs | |
|:----------|:-----------------------------------------------------|:---:|:---:|:-----------:|:----:| |
| BOOT ROM | Bootloader ROM (default 4kB) | 4 | 1 | 32 768 | 0 | |
| BUSSWITCH | Mux for CPU I & D interfaces | 62 | 8 | 0 | 0 | |
| CFU | Custom functions unit | - | - | - | - | |
| DEVNULL | Dummy device | 3 | 1 | 0 | 0 | |
| DMEM | Processor-internal data memory (default 8kB) | 12 | 2 | 65 536 | 0 | |
| GPIO | General purpose input/output ports | 40 | 33 | 0 | 0 | |
| IMEM | Processor-internal instruction memory (default 16kb) | 7 | 2 | 131 072 | 0 | |
| MTIME | Machine system timer | 266 | 166 | 0 | 0 | |
| PWM | Pulse-width modulation controller | 72 | 69 | 0 | 0 | |
| SPI | Serial peripheral interface | 198 | 125 | 0 | 0 | |
| SYSINFO | System configuration information memory | 10 | 9 | 0 | 0 | |
| TRNG | True random number generator | 105 | 93 | 0 | 0 | |
| TWI | Two-wire interface | 75 | 44 | 0 | 0 | |
| UART | Universal asynchronous receiver/transmitter | 153 | 108 | 0 | 0 | |
| WDT | Watchdog timer | 59 | 45 | 0 | 0 | |
|
|
### NEORV32 Processor - Exemplary FPGA Setups |
566,7 → 566,7
[`rtl/top_templates`](https://github.com/stnolting/neorv32/blob/master/rtl/top_templates) folder) as top entity. |
|
This test setup instantiates the processor and implements most of the peripherals and some ISA extensions. Only the UART lines, clock, reset and some GPIO output sginals are |
propagated as actual entity signals. Basically, its a FPGA "hello world" example: |
propagated as actual entity signals. Basically, it is a FPGA "hello world" example: |
|
```vhdl |
entity neorv32_test_setup is |