Related reference articles:
RISC-V teaching plan
This article then introduces the implementation of the register on the RISC-V CSR read and write control (3) CSR register implementation.
1.4. csr_mepc
The mepc register stores the PC value when an interrupt or exception occurs, which will be used as the return address in the exception subroutine and is a readable and writable register. Part of the important code to implement the mepc register is as follows (the code repeated in the previous module is omitted):
input i_irq_src,//Interrupt source input i_exp_src,//Exception source input [ 31: 0 ] i_exe_pc,//current PC output [ 31: 0 ] o_mepc,//output mepc wire [ 31: 0 ] i_mepc; wire [ 31: 0 ] mepc_r; wire mepc_valid = (i_irq_src | i_exp_src) ? 1'b1 : 1'b0;//Interrupt or exception arrives assign i_mepc = mepc_valid ? i_exe_pc : mepc_r;//If there is an interrupt/abnormal arrival, set the PC as the current PC, otherwise use the previously stored mepc wire sel_mepc = ( i_csr_addr == 12'h341 );//Confirm register by address index wire mepc_ena = wr_mepc | mepc_valid;//Enable written by CSR instruction or set by interrupt/abnormal wire [ 31: 0 ] mepc_nxt; assign mepc_nxt[ 31: 1 ] = mepc_valid ? i_mepc[ 31 : 1 ] : i_csr_val[ 31 : 1 ];//If there is an interrupt, write the return address of the interrupt, otherwise it will be written by the CSR instruction assign mepc_nxt[ 0 ] = 1'b0; // The address of mepc must be aligned with at least 2 bytes, otherwise an address misalignment exception may be generated fii_dfflr #( 32 ) epc_dfflr ( mepc_ena, mepc_nxt, mepc_r, sys_clk, rst_n );//Latch assign o_mepc = mepc_r;//output
1.5 csr_mcause
mcause is a readable and writable register that records the reason for the last entry exception or interrupt time and serves the interrupt or exception subroutine in the form of a record number (exception code). The definition of its specific value can be found in Figure 8 in the RISC-V CSR register (2) CSR register. mcause will also be used in conjunction with mtvec. If the MODE of mtvec is 1, the entry address of the interrupt will become BASE + 4 X cause, where the cause is the exception code of the interrupt ( when an exception occurs, the interrupt entry address is still the base address. ). Part of the important code to implement the mcause register is as follows (the code repeated in the previous module is omitted):
input i_irq_src,//Interrupt source input i_mie,//interrupt global enable input i_meie, // external interrupt enable in machine mode input i_mtie, // timer interrupt enable in machine mode input i_msie,//Software interrupt enable in machine mode input i_sft_irq,//software interrupt request input i_tmr_irq,//Timer interrupt request input i_ext_irq,//External interrupt request input i_EXE_vld,//Whether it is the execution stage input i_exp_src,//Exception source //The following are some exceptions, currently not implemented, hardwired to 0 in the outer csr_reg module. input i_iam_exp, input i_iaf_exp, input i_illi_exp, input i_bp_exp, input i_mti_exp, input i_lam_exp, input i_laf_exp, input i_saam_exp, input i_saaf_exp, output [ 31: 0 ] o_mcause,//output mcause wire m_is = i_mie & i_msie & i_sft_irq;//software interrupt wire m_it = i_mie & i_mtie & i_tmr_irq;//Timer interrupt wire m_ie = i_mie & i_meie & i_ext_irq;//External interrupt //==============================Exceptions and Interrupts================ ===================================== wire[31:0] exp_mcause; //When an exception occurs, the highest bit of mcause is 0 assign exp_mcause[31:5] = 27'b0; //According to the type of exception, set the corresponding bit assign exp_mcause[4:0] = i_iam_exp ? 5'd0 //Instruction address misaligned : i_iaf_exp ? 5'd1 //Instruction access fault : i_illi_exp ? 5'd2 //Illegal instruction : i_bp_exp ? 5'd3 //Breakpoint : i_lam_exp ? 5'd4 //load address misalign : i_laf_exp ? 5'd5 //load access fault : i_saam_exp ? 5'd6 //Store/AMO address misalign : i_saaf_exp ? 5'd7 //Store/AMO access fault : 5'h1F; //Otherwise a reserved value wire[31:0] irq_mcause; //When an interrupt occurs, the highest bit of mcause is 1 assign irq_mcause[31] = 1'b1; assign irq_mcause[30:4] = 27'b0; //According to the type of interrupt, set the corresponding bit assign irq_mcause[3:0] = m_is ? 4'd3 : // 3 Machine software interrupt m_it ? 4'd7 : // 7 Machine timer interrupt m_ie ? 4'd11 : // 11 Machine external interrupt 4'b0; wire [31:0] mcause_val = i_irq_src ? irq_mcause : exp_mcause;//Determine interrupt or exception //==================================================== ================================= wire mcause_valid = (i_irq_src | i_exp_src) ? 1'b1 : 1'b0;//Whether interrupt/exception occurs wire sel_mcause = (i_csr_addr == 12'h342);//Confirm register by address index wire[31:0] mcause_r; wire [31:0] mcause_nxt = mcause_valid ? mcause_val : i_csr_val;//If an interrupt/exception occurs, mcause is assigned as the cause of the interrupt/abnormal, otherwise it is written by the CSR instruction fii_dfflr #(32) mcause_dfflr (mcause_ena, mcause_nxt, mcause_r, sys_clk, rst_n);//Latch assign o_mcause = mcause_r;//output
1.6. csr_mie
The mie register is readable and writable and is used for further separate enable control of different interrupts. Note that it is separated from the bit MIE of mstatus. Part of the important code to implement the mie register is as follows (the code repeated in the previous module is omitted):
output [ 31: 0 ] o_mie,//output mie output o_meie,// external interrupt enable in output machine mode output o_msie,//Software interrupt enable in output machine mode output o_mtie, // timer interrupt enable in output machine mode wire sel_mie = ( i_csr_addr == 12'h304 );//Confirm the register by the address index wire [ 31: 0 ] mie_r; wire [ 31: 0 ] mie_nxt; assign mie_nxt[ 31: 12 ] = 20'b0;//hardwire other bits to 0 assign mie_nxt[ 11 ] = i_csr_val[ 11 ]; //o_meie assign mie_nxt[ 10: 8 ] = 3'b0; assign mie_nxt[ 7 ] = i_csr_val[ 7 ]; //o_mtie assign mie_nxt[ 6: 4 ] = 3'b0; assign mie_nxt[ 3 ] = i_csr_val[ 3 ]; //o_msie assign mie_nxt[ 2: 0 ] = 3'b0; fii_dfflr #( 32 ) mie_dfflr ( mie_ena, mie_nxt, mie_r, sys_clk, rst_n );//Latch assign o_mie = mie_r;//output // Output the important interrupt enable bit separately assign o_meie = o_mie[ 11 ]; assign o_mtie = o_mie[7]; assign o_msie = o_mie[3];
1.7. csr_mip
Although the mip registers are by definition readable and writable, the MEIP, MSIP and MTIP used here are read-only. Part of the important code to implement the mip register is as follows (the code repeated in the previous module is omitted):
input i_sft_irq,//software interrupt request input i_tmr_irq,//Timer interrupt request input i_ext_irq,//External interrupt request output [ 31: 0 ] o_mip,//output mip wire sel_mip = ( i_csr_addr == 12'h344 );//Confirm the register by the address index wire meip_r; wire msip_r; wire mtip_r; //As long as there is an interrupt request, the corresponding interrupt hanging bit will be set assign meip_r = i_ext_irq; assign msip_r = i_sft_irq; assign mtip_r = i_tmr_irq; //Assign the bits in the mip respectively. wire [ 31: 0 ] ip_r; assign ip_r[ 31: 12 ] = 20'b0; assign ip_r[ 11 ] = meip_r; assign ip_r[ 10: 8 ] = 3'b0; assign ip_r[ 7 ] = mtip_r; assign ip_r[ 6: 4 ] = 3'b0; assign ip_r[ 3 ] = msip_r; assign ip_r[ 2: 0 ] = 3'b0; assign o_mip = ip_r;//output