The code used in this article is based on FII RISC-V V2.01 (without JTAG and bus). For the upper-level module of csr_reg, see RISC-V CSR read-write control (1) exu_csr module , here will mainly introduce the csr_reg module, and continue the study of CSR read-write control.
Related reference articles:
RISC-V teaching plan
1. Input and output
input sys_clk, //system clock //--------------- Debug related signals are also omitted here--------------- input i_EXE_vld, //execute valid input i_ext_irq, //External interrupt request input i_sft_irq, //software interrupt request input i_tmr_irq, //Timer interrupt request input i_irq_src, //interrupt source input i_exp_src, //exception source input [ 31: 0 ] i_exe_pc, //executed program counter input [ 31: 0 ] i_ir, //instruction register output [ 31: 0 ] o_mepc, //PC that needs to be executed after the interrupt returns output [ 31: 0 ] o_irq_pc, //Interrupt entry address input i_csr_rden, //CSR register can be read input [ 11: 0 ] i_csr_addr, //csr instruction (one of 6) input [ 31: 0 ] i_csr_val, //The value written by the CSR instruction to the CSR register input i_csr_wen, //CSR register can be written output [ 31: 0 ] o_csr_val, //The value read from the CSR register output o_meie, //external interrupt enable output o_msie, //software interrupt enable output o_mtie, //timer interrupt enable output o_glb_irq, //Global interrupt enable input rst_n //reset
As can be seen from the above code, the signal of the csr_reg module is basically similar to the signal in exu_csr, except that the readable/writable signal that defines the CSR register attribute in the exu_csr module is passed in, and the CSR instruction is directly written to the CSR register The value of is passed in, and the operation of the CSR instruction is not performed.
2. Execute
In addition to the declaration and transmission of some signals, the main body of the code also reads the CSR register. The code is as follows:
assign o_csr_val = csr_reh_sel; //The value read by the CSR register always@( * ) //CSR read case ( i_csr_addr & { 12{ i_csr_rden } } ) 12'h300: csr_reh_sel = w_mstatus; 12'h301: csr_reh_sel = w_misa; 12'h304: csr_reh_sel = w_mie; 12'h305: csr_reh_sel = w_mtvec; 12'h306: csr_reh_sel = w_mcounteren; 12'hf11: csr_reh_sel = w_mvendorid; 12'hf12: csr_reh_sel = w_marchid; 12'hf13: csr_reh_sel = w_mimpid; 12'hf14: csr_reh_sel = w_mhartid; 12'h340: csr_reh_sel = w_mscratch; 12'h341: csr_reh_sel = o_mepc; 12'h342: csr_reh_sel = w_mcause; 12'h343: csr_reh_sel = w_mtval; 12'h344: csr_reh_sel = w_mip; 12'hb00: csr_reh_sel = w_mcycle_l; 12'hb80: csr_reh_sel = w_mcycle_h; 12'hb02: csr_reh_sel = w_minstret_l; 12'hb82: csr_reh_sel = w_minstret_h; 12'h7b0: csr_reh_sel = i_dcsr; 12'h7b1: csr_reh_sel = i_dpc; 12'h7b2: csr_reh_sel = i_dscratch; default: csr_reh_sel = 32'b0; endcase
It can be seen that by judging the index of the CSR address and the readable attribute of the CSR, the values of different CSR registers are read out in different cases.
Note: The case selection here, although 21 are listed, in each CSR instruction read, only one exact CSR register is read, that is, a CSR instruction will only be based on the required address. Only one case is executed at a time.
case ( i_csr_addr & { 12{ i_csr_rden } } )
In this way of writing, i_csr_rden of { 12{ i_csr_rden } } is originally a 1-bit signal, and it is expanded to 12 bits here to facilitate the operation with the CSR address. E.g:
- If the CSR register is readable, i_csr_rden is 1, { 12{ i_csr_rden } } is 12’b1111_1111_1111, and the value obtained by adding i_csr_addr is i_csr_addr itself , starting case selection;
- If the CSR register is not readable, i_csr_rden is 0, { 12{ i_csr_rden } } is 12’b00000_0000_0000, and the value obtained by adding i_csr_addr is 0, any case is not satisfied, jump to the default statement, that is, set the CSR register The read value is assigned 0.
assign o_glb_irq = w_mstatus[3]; //Global interrupt enable wire [ 31: 0 ] vect_pc = {w_mtvec[ 31: 2 ], 2'b00} + {w_mcause[ 3: 0 ], 2'b00}; // Mode1, vector address = base + 4 X cause assign o_irq_pc = ( w_mtvec[ 1: 0 ] == 0 ) ? {w_mtvec[ 31: 2 ], 2'b00}://select interrupt entry address ( w_mtvec[ 1 : 0 ] == 1 ) ? vect_pc : o_irq_pc; wire status_ena = w_mstatus[3] & ( o_meie | o_mtie | o_msie ) & i_irq_src;//Interrupt
The above code is about outputting the global interrupt enable o_glb_irq and passing the ststus_ena interrupt generation signal to the mstatus register for the judgment of MPIE and MIE bits.
o_irq_pc is the entry address of the interrupt, and the output Mode is determined by bits 0-1 of w_mtvec
- When Mode is 1, the interrupt entry address is the vector address, which is composed of base base address + 4 X cause (determined by mcause register)
- When Mode is 0, the interrupt entry is directly assigned to the base base address
- If Mode is equal to 2 or 3, the value is reserved, currently undefined
Note: There is only a 30-bit base address in bits 2-31 of the mtvec register, because the default interrupt jump addresses are all 4-byte aligned [1]. So the real interrupt entry address needs to be shifted left by 2 bits, which is {w_mtvec[ 31: 2 ], 2’b00 } in the above code .
3. CSR register example
The instance under the csr_reg module consists of all implemented CSR registers, with the following register modules:
- csr_mtvec
- csr_mstatus
- csr_mtval
- csr_mepc
- csr_mcause
- csr_mie
- csr_mip
- csr_misa
- csr_mcounteren
- csr_mid: includes mvendorid, marchid, mimpid and mhartid
- csr_scratch: mscratch register
- csr_mcycle: 32-bit registers including low-order and high-order bits, forming a total of 64 bits
- csr_minstret: 32-bit registers including low-order and high-order bits, forming a total of 64 bits
- dug_csr: includes some debugging-related signals
The specific implementation will be introduced later.
4. Article references
[1] Riscv.org , 2021. [Online]. Available: https://riscv.org/wp-content/uploads/2019/12/riscv-spec-20191213.pdf. [Accessed: 22- Feb- 2021] .