Signed-off-by: Michael Walle <mich...@walle.cc> --- Changes: v2: - fix indentation - add defaults for PC_ex and next_irq_ack to avoid inferring latches
cores/softusb/navre_regress/tb_regress.v | 2 + .../navre_regress/test_opcodes/test_BCLR.py | 2 +- .../softusb/navre_regress/test_opcodes/test_BST.py | 2 +- .../navre_regress/test_opcodes/test_RETI.py | 2 +- cores/softusb/rtl/softusb.v | 2 + cores/softusb/rtl/softusb_navre.v | 417 +++++++++++++------- cores/softusb/test/tb_navre.v | 2 + 7 files changed, 279 insertions(+), 150 deletions(-) diff --git a/cores/softusb/navre_regress/tb_regress.v b/cores/softusb/navre_regress/tb_regress.v index 76c68ed..d00866b 100644 --- a/cores/softusb/navre_regress/tb_regress.v +++ b/cores/softusb/navre_regress/tb_regress.v @@ -99,6 +99,8 @@ softusb_navre #( .io_do(io_do), .io_di(io_di), + .irq(8'b0), + .dbg_pc() ); diff --git a/cores/softusb/navre_regress/test_opcodes/test_BCLR.py b/cores/softusb/navre_regress/test_opcodes/test_BCLR.py index a667ac7..74c9044 100644 --- a/cores/softusb/navre_regress/test_opcodes/test_BCLR.py +++ b/cores/softusb/navre_regress/test_opcodes/test_BCLR.py @@ -51,7 +51,7 @@ class base_BCLR(base_test.opcode_test): def analyze_results(self): # check that correct SREG bit is cleared self.reg_changed.append(Reg.SREG) - expect = 0x7f & ~(1 << self.bit) + expect = 0xff & ~(1 << self.bit) got = self.anal_regs[Reg.SREG] if expect != got: self.fail('SREG bit %d not set: expect=%02x, got=%02x' % (self.bit, expect, got)) diff --git a/cores/softusb/navre_regress/test_opcodes/test_BST.py b/cores/softusb/navre_regress/test_opcodes/test_BST.py index 2994fee..58c7ab7 100644 --- a/cores/softusb/navre_regress/test_opcodes/test_BST.py +++ b/cores/softusb/navre_regress/test_opcodes/test_BST.py @@ -55,7 +55,7 @@ class base_BST(base_test.opcode_test): # check that register value is correct if self.T == 0: - expect = 0x7f & ~(1 << SREG.T) + expect = 0xff & ~(1 << SREG.T) else: expect = (1 << SREG.T) diff --git a/cores/softusb/navre_regress/test_opcodes/test_RETI.py b/cores/softusb/navre_regress/test_opcodes/test_RETI.py index 7466903..1b147ce 100644 --- a/cores/softusb/navre_regress/test_opcodes/test_RETI.py +++ b/cores/softusb/navre_regress/test_opcodes/test_RETI.py @@ -72,7 +72,7 @@ class base_RETI(base_test.opcode_stack_test): expect, got )) # check no SREG flag changed - expect = 0x00 + expect =0x1 << SREG.I got = self.anal_regs[Reg.SREG] if got != expect: diff --git a/cores/softusb/rtl/softusb.v b/cores/softusb/rtl/softusb.v index 851f31c..79eec04 100644 --- a/cores/softusb/rtl/softusb.v +++ b/cores/softusb/rtl/softusb.v @@ -185,6 +185,8 @@ softusb_navre #( .io_do(io_dw), .io_di(io_dr_sie|io_dr_timer), + .irq(8'b0), + .dbg_pc(dbg_pc) ); diff --git a/cores/softusb/rtl/softusb_navre.v b/cores/softusb/rtl/softusb_navre.v index c7f8adc..38a971f 100644 --- a/cores/softusb/rtl/softusb_navre.v +++ b/cores/softusb/rtl/softusb_navre.v @@ -37,6 +37,9 @@ module softusb_navre #( output [7:0] io_do, input [7:0] io_di, + input [7:0] irq, + output reg [7:0] irq_ack, + output reg [pmem_width-1:0] dbg_pc ); @@ -47,7 +50,7 @@ reg [15:0] U; /* < R24-R25 */ reg [15:0] pX; /* < R26-R27 */ reg [15:0] pY; /* < R28-R29 */ reg [15:0] pZ; /* < R30-R31 */ -reg T, H, S, V, N, Z, C; +reg I, T, H, S, V, N, Z, C; /* Stack */ reg [7:0] io_sp; @@ -178,6 +181,65 @@ parameter PC_SEL_DMEML = 4'd4; parameter PC_SEL_DMEMH = 4'd6; parameter PC_SEL_DEC = 4'd7; parameter PC_SEL_Z = 4'd8; +parameter PC_SEL_EX = 4'd9; + +/* Exceptions */ + +reg [7:0] next_irq_ack; +always @(*) begin + next_irq_ack = 8'b0; + casex(irq) + 8'bxxxx_xxx1: next_irq_ack = 8'b0000_0001; + 8'bxxxx_xx10: next_irq_ack = 8'b0000_0010; + 8'bxxxx_x100: next_irq_ack = 8'b0000_0100; + 8'bxxxx_1000: next_irq_ack = 8'b0000_1000; + 8'bxxx1_0000: next_irq_ack = 8'b0001_0000; + 8'bxx10_0000: next_irq_ack = 8'b0010_0000; + 8'bx100_0000: next_irq_ack = 8'b0100_0000; + 8'b1000_0000: next_irq_ack = 8'b1000_0000; + endcase +end + +reg irq_ack_en; +always @(posedge clk) begin + if(rst) + irq_ack <= 8'b0; + else begin + irq_ack <= 1'b0; + if(irq_ack_en) + irq_ack <= next_irq_ack; + end +end + +/* Priority encoder */ + +reg [3:0] PC_ex; +always @(*) begin + PC_ex = 4'b0; + casex(irq) + 8'bxxxx_xxx1: PC_ex = 4'h0; + 8'bxxxx_xx10: PC_ex = 4'h1; + 8'bxxxx_x100: PC_ex = 4'h2; + 8'bxxxx_1000: PC_ex = 4'h3; + 8'bxxx1_0000: PC_ex = 4'h4; + 8'bxx10_0000: PC_ex = 4'h5; + 8'bx100_0000: PC_ex = 4'h6; + 8'b1000_0000: PC_ex = 4'h7; + endcase +end + +/* AVR cores always execute at least one instruction after an IRET. + * Therefore, the I bit is only valid one clock after it has been set. */ + +reg I_r; +always @(posedge clk) begin + if(rst) + I_r <= 1'b0; + else + I_r <= I; +end +wire irq_asserted = |irq; +wire irq_request = I & I_r & irq_asserted; always @(posedge clk) begin if(rst) begin @@ -195,6 +257,7 @@ always @(posedge clk) begin PC_SEL_DMEMH: PC[pmem_width-1:8] <= dmem_di; PC_SEL_DEC: PC <= PC - 1; PC_SEL_Z: PC <= pZ - 1; + PC_SEL_EX: PC <= {{pmem_width-4{1'b0}}, PC_ex}; endcase end dbg_pc <= PC; @@ -245,6 +308,8 @@ reg mode16; reg _N; reg _V; reg _C; +reg I_clr; +reg I_set; always @(posedge clk) begin R = 8'hxx; writeback = 1'b0; @@ -266,6 +331,7 @@ always @(posedge clk) begin pY <= 16'd0; pZ <= 16'd0; // synthesis translate_on + I <= 1'b0; T <= 1'b0; H <= 1'b0; S <= 1'b0; @@ -278,6 +344,8 @@ always @(posedge clk) begin _C = 1'b0; `endif end else begin + if(I_set) + I <= 1'b1; if(normal_en) begin writeback = 1'b1; update_svnz = 1'b1; @@ -386,6 +454,7 @@ always @(posedge clk) begin 4'b0100: S <= 1'b1; 4'b0101: H <= 1'b1; 4'b0110: T <= 1'b1; + 4'b0111: I <= 1'b1; 4'b1000: C <= 1'b0; 4'b1001: Z <= 1'b0; 4'b1010: N <= 1'b0; @@ -393,6 +462,7 @@ always @(posedge clk) begin 4'b1100: S <= 1'b0; 4'b1101: H <= 1'b0; 4'b1110: T <= 1'b0; + 4'b1111: I <= 1'b0; endcase update_svnz = 1'b0; writeback = 1'b0; @@ -475,7 +545,7 @@ always @(posedge clk) begin case(io_sel) IO_SEL_EXT: R = io_di; IO_SEL_STACK: R = io_sp; - IO_SEL_SREG: R = {1'b0, T, H, S, V, N, Z, C}; + IO_SEL_SREG: R = {I, T, H, S, V, N, Z, C}; default: R = 8'hxx; endcase update_svnz = 1'b0; @@ -498,7 +568,9 @@ always @(posedge clk) begin Z <= mode16 ? R16 == 16'h0000 : ((R == 8'h00) & (change_z|Z)); end if(io_we & (io_a == 6'b111111)) - {T, H, S, V, N, Z, C} <= io_do[6:0]; + {I, T, H, S, V, N, Z, C} <= io_do[7:0]; + if(I_clr) + I <= 1'b0; if(writeback) begin if(mode16) begin // synthesis translate_off @@ -565,6 +637,7 @@ always @(*) begin end wire [pmem_width-1:0] PC_inc = PC + 1; +reg exception; always @(*) begin case(dmem_sel) DMEM_SEL_X, @@ -577,8 +650,8 @@ always @(*) begin DMEM_SEL_ZMINUS, DMEM_SEL_ZQ, DMEM_SEL_SP_R: dmem_do = GPR_Rd; - DMEM_SEL_SP_PCL: dmem_do = PC_inc[7:0]; - DMEM_SEL_SP_PCH: dmem_do = PC_inc[pmem_width-1:8]; + DMEM_SEL_SP_PCL: dmem_do = exception ? PC[7:0] : PC_inc[7:0]; + DMEM_SEL_SP_PCH: dmem_do = exception ? PC[pmem_width-1:8] : PC_inc[pmem_width-1:8]; DMEM_SEL_PMEM: dmem_do = GPR_Rd_r; default: dmem_do = 8'hxx; endcase @@ -598,26 +671,31 @@ always @(*) begin 3'd4: sreg_read = S; 3'd5: sreg_read = H; 3'd6: sreg_read = T; - 3'd7: sreg_read = 1'b0; + 3'd7: sreg_read = I; endcase end -reg [3:0] state; -reg [3:0] next_state; - -parameter NORMAL = 4'd0; -parameter RCALL = 4'd1; -parameter ICALL = 4'd2; -parameter STALL = 4'd3; -parameter RET1 = 4'd4; -parameter RET2 = 4'd5; -parameter RET3 = 4'd6; -parameter LPM = 4'd7; -parameter STS = 4'd8; -parameter LDS1 = 4'd9; -parameter LDS2 = 4'd10; -parameter SKIP = 4'd11; -parameter WRITEBACK = 4'd12; +reg [4:0] state; +reg [4:0] next_state; + +parameter NORMAL = 5'd0; +parameter RCALL = 5'd1; +parameter ICALL = 5'd2; +parameter STALL = 5'd3; +parameter RET1 = 5'd4; +parameter RET2 = 5'd5; +parameter RET3 = 5'd6; +parameter LPM = 5'd7; +parameter STS = 5'd8; +parameter LDS1 = 5'd9; +parameter LDS2 = 5'd10; +parameter SKIP = 5'd11; +parameter WRITEBACK = 5'd12; +parameter EXCEPTION = 5'd13; +parameter RETI1 = 5'd14; +parameter RETI2 = 5'd15; +parameter RETI3 = 5'd16; +parameter RETI4 = 5'd17; always @(posedge clk) begin if(rst) @@ -644,6 +722,11 @@ always @(*) begin push = 1'b0; pop = 1'b0; + exception = 1'b0; + irq_ack_en = 1'b0; + I_set = 1'b0; + I_clr = 1'b0; + pmem_selz = 1'b0; regmem_ce = 1'b1; @@ -651,144 +734,157 @@ always @(*) begin case(state) NORMAL: begin - casex(pmem_d) - 16'b1100_xxxx_xxxx_xxxx: begin - /* RJMP */ - pc_sel = PC_SEL_KL; - next_state = STALL; - end - 16'b1101_xxxx_xxxx_xxxx: begin - /* RCALL */ - dmem_sel = DMEM_SEL_SP_PCL; - dmem_we = 1'b1; - push = 1'b1; - next_state = RCALL; - end - 16'b0001_00xx_xxxx_xxxx: begin - /* CPSE */ - pc_sel = PC_SEL_INC; - pmem_ce = 1'b1; - if(reg_equal) - next_state = SKIP; - end - 16'b1111_11xx_xxxx_0xxx: begin - /* SBRC - SBRS */ - pc_sel = PC_SEL_INC; - pmem_ce = 1'b1; - if(GPR_Rd_b == pmem_d[9]) - next_state = SKIP; - end - /* SBIC, SBIS, SBI, CBI are not implemented */ - 16'b1111_0xxx_xxxx_xxxx: begin - /* BRBS - BRBC */ - pmem_ce = 1'b1; - if(sreg_read ^ pmem_d[10]) begin - pc_sel = PC_SEL_KS; + if(irq_request) begin + dmem_sel = DMEM_SEL_SP_PCL; + dmem_we = 1'b1; + exception = 1'b1; + push = 1'b1; + irq_ack_en = 1'b1; + I_clr = 1'b1; + next_state = EXCEPTION; + end else begin + casex(pmem_d) + 16'b1100_xxxx_xxxx_xxxx: begin + /* RJMP */ + pc_sel = PC_SEL_KL; next_state = STALL; - end else + end + 16'b1101_xxxx_xxxx_xxxx: begin + /* RCALL */ + dmem_sel = DMEM_SEL_SP_PCL; + dmem_we = 1'b1; + push = 1'b1; + next_state = RCALL; + end + 16'b0001_00xx_xxxx_xxxx: begin + /* CPSE */ pc_sel = PC_SEL_INC; - end - /* BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT, - * BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRID are replaced - * with BRBS/BRBC */ - 16'b1001_00xx_xxxx_1100, /* X */ - 16'b1001_00xx_xxxx_1101, /* X+ */ - 16'b1001_00xx_xxxx_1110, /* -X */ - 16'b1001_00xx_xxxx_1001, /* Y+ */ - 16'b1001_00xx_xxxx_1010, /* -Y */ - 16'b10x0_xxxx_xxxx_1xxx, /* Y+q */ - 16'b1001_00xx_xxxx_0001, /* Z+ */ - 16'b1001_00xx_xxxx_0010, /* -Z */ - 16'b10x0_xxxx_xxxx_0xxx: /* Z+q */ - begin - casex({pmem_d[12], pmem_d[3:0]}) - 5'b1_1100: dmem_sel = DMEM_SEL_X; - 5'b1_1101: dmem_sel = DMEM_SEL_XPLUS; - 5'b1_1110: dmem_sel = DMEM_SEL_XMINUS; - 5'b1_1001: dmem_sel = DMEM_SEL_YPLUS; - 5'b1_1010: dmem_sel = DMEM_SEL_YMINUS; - 5'b0_1xxx: dmem_sel = DMEM_SEL_YQ; - 5'b1_0001: dmem_sel = DMEM_SEL_ZPLUS; - 5'b1_0010: dmem_sel = DMEM_SEL_ZMINUS; - 5'b0_0xxx: dmem_sel = DMEM_SEL_ZQ; - endcase - if(pmem_d[9]) begin - /* ST */ + pmem_ce = 1'b1; + if(reg_equal) + next_state = SKIP; + end + 16'b1111_11xx_xxxx_0xxx: begin + /* SBRC - SBRS */ pc_sel = PC_SEL_INC; pmem_ce = 1'b1; - dmem_we = 1'b1; - end else begin - /* LD */ - next_state = WRITEBACK; + if(GPR_Rd_b == pmem_d[9]) + next_state = SKIP; end - end - 16'b1011_0xxx_xxxx_xxxx: begin - /* IN */ - io_re = 1'b1; - next_state = WRITEBACK; - end - 16'b1011_1xxx_xxxx_xxxx: begin - /* OUT */ - io_we = 1'b1; - pc_sel = PC_SEL_INC; - pmem_ce = 1'b1; - end - 16'b1001_00xx_xxxx_xxxx: begin - if(pmem_d[3:0] == 4'hf) begin + /* SBIC, SBIS, SBI, CBI are not implemented */ + 16'b1111_0xxx_xxxx_xxxx: begin + /* BRBS - BRBC */ + pmem_ce = 1'b1; + if(sreg_read ^ pmem_d[10]) begin + pc_sel = PC_SEL_KS; + next_state = STALL; + end else + pc_sel = PC_SEL_INC; + end + /* BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT, + * BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRID are replaced + * with BRBS/BRBC */ + 16'b1001_00xx_xxxx_1100, /* X */ + 16'b1001_00xx_xxxx_1101, /* X+ */ + 16'b1001_00xx_xxxx_1110, /* -X */ + 16'b1001_00xx_xxxx_1001, /* Y+ */ + 16'b1001_00xx_xxxx_1010, /* -Y */ + 16'b10x0_xxxx_xxxx_1xxx, /* Y+q */ + 16'b1001_00xx_xxxx_0001, /* Z+ */ + 16'b1001_00xx_xxxx_0010, /* -Z */ + 16'b10x0_xxxx_xxxx_0xxx: /* Z+q */ + begin + casex({pmem_d[12], pmem_d[3:0]}) + 5'b1_1100: dmem_sel = DMEM_SEL_X; + 5'b1_1101: dmem_sel = DMEM_SEL_XPLUS; + 5'b1_1110: dmem_sel = DMEM_SEL_XMINUS; + 5'b1_1001: dmem_sel = DMEM_SEL_YPLUS; + 5'b1_1010: dmem_sel = DMEM_SEL_YMINUS; + 5'b0_1xxx: dmem_sel = DMEM_SEL_YQ; + 5'b1_0001: dmem_sel = DMEM_SEL_ZPLUS; + 5'b1_0010: dmem_sel = DMEM_SEL_ZMINUS; + 5'b0_0xxx: dmem_sel = DMEM_SEL_ZQ; + endcase if(pmem_d[9]) begin - /* PUSH */ - push = 1'b1; - dmem_sel = DMEM_SEL_SP_R; - dmem_we = 1'b1; + /* ST */ pc_sel = PC_SEL_INC; pmem_ce = 1'b1; + dmem_we = 1'b1; end else begin - /* POP */ - pop = 1'b1; - dmem_sel = DMEM_SEL_SP_R; + /* LD */ next_state = WRITEBACK; end - end else if(pmem_d[3:0] == 4'h0) begin + end + 16'b1011_0xxx_xxxx_xxxx: begin + /* IN */ + io_re = 1'b1; + next_state = WRITEBACK; + end + 16'b1011_1xxx_xxxx_xxxx: begin + /* OUT */ + io_we = 1'b1; pc_sel = PC_SEL_INC; pmem_ce = 1'b1; - if(pmem_d[9]) - /* STS */ - next_state = STS; + end + 16'b1001_00xx_xxxx_xxxx: begin + if(pmem_d[3:0] == 4'hf) begin + if(pmem_d[9]) begin + /* PUSH */ + push = 1'b1; + dmem_sel = DMEM_SEL_SP_R; + dmem_we = 1'b1; + pc_sel = PC_SEL_INC; + pmem_ce = 1'b1; + end else begin + /* POP */ + pop = 1'b1; + dmem_sel = DMEM_SEL_SP_R; + next_state = WRITEBACK; + end + end else if(pmem_d[3:0] == 4'h0) begin + pc_sel = PC_SEL_INC; + pmem_ce = 1'b1; + if(pmem_d[9]) + /* STS */ + next_state = STS; + else + /* LDS */ + next_state = LDS1; + end + end + 16'b1001_0101_000x_1000: begin + /* RET / RETI */ + dmem_sel = DMEM_SEL_SP_PCH; + pop = 1'b1; + if(pmem_d[4] == 1'b0) + next_state = RET1; else - /* LDS */ - next_state = LDS1; + next_state = RETI1; end - end - 16'b1001_0101_000x_1000: begin - /* RET - RETI (treated as RET) */ - dmem_sel = DMEM_SEL_SP_PCH; - pop = 1'b1; - next_state = RET1; - end - 16'b1001_0101_1100_1000: begin - /* LPM */ - pmem_selz = 1'b1; - pmem_ce = 1'b1; - next_state = LPM; - end - 16'b1001_0100_0000_1001: begin - /* IJMP */ - pc_sel = PC_SEL_Z; - next_state = STALL; - end - 16'b1001_0101_0000_1001: begin - /* ICALL */ - dmem_sel = DMEM_SEL_SP_PCL; - dmem_we = 1'b1; - push = 1'b1; - next_state = ICALL; - end - default: begin - pc_sel = PC_SEL_INC; - normal_en = 1'b1; - pmem_ce = 1'b1; - end - endcase + 16'b1001_0101_1100_1000: begin + /* LPM */ + pmem_selz = 1'b1; + pmem_ce = 1'b1; + next_state = LPM; + end + 16'b1001_0100_0000_1001: begin + /* IJMP */ + pc_sel = PC_SEL_Z; + next_state = STALL; + end + 16'b1001_0101_0000_1001: begin + /* ICALL */ + dmem_sel = DMEM_SEL_SP_PCL; + dmem_we = 1'b1; + push = 1'b1; + next_state = ICALL; + end + default: begin + pc_sel = PC_SEL_INC; + normal_en = 1'b1; + pmem_ce = 1'b1; + end + endcase + end end RCALL: begin dmem_sel = DMEM_SEL_SP_PCH; @@ -797,6 +893,14 @@ always @(*) begin pc_sel = PC_SEL_KL; next_state = STALL; end + EXCEPTION: begin + dmem_sel = DMEM_SEL_SP_PCH; + dmem_we = 1'b1; + exception = 1'b1; + push = 1'b1; + pc_sel = PC_SEL_EX; + next_state = STALL; + end ICALL: begin dmem_sel = DMEM_SEL_SP_PCH; dmem_we = 1'b1; @@ -818,6 +922,26 @@ always @(*) begin pc_sel = PC_SEL_DEC; next_state = STALL; end + RETI1: begin + pc_sel = PC_SEL_DMEMH; + dmem_sel = DMEM_SEL_SP_PCL; + pop = 1'b1; + next_state = RETI2; + end + RETI2: begin + pc_sel = PC_SEL_DMEML; + next_state = RETI3; + end + RETI3: begin + pc_sel = PC_SEL_DEC; + next_state = RETI4; + end + RETI4: begin + pc_sel = PC_SEL_INC; + pmem_ce = 1'b1; + I_set = 1'b1; + next_state = NORMAL; + end LPM: begin lpm_en = 1'b1; pc_sel = PC_SEL_INC; @@ -881,7 +1005,7 @@ always @(posedge clk) begin $display("%x", pY[15:8]); $display("%x", pZ[7:0]); $display("%x", pZ[15:8]); - $display("%x", {1'b0, T, H, S, V, N, Z, C}); + $display("%x", {I, T, H, S, V, N, Z, C}); $display("%x", SP[15:8]); $display("%x", SP[7:0]); $display("%x", PC[pmem_width-1:7]); @@ -896,7 +1020,6 @@ always @(posedge clk) begin end reg [7:0] SPR[0:12]; -reg I; initial begin $readmemh("gpr.rom", GPR); $readmemh("spr.rom", SPR); diff --git a/cores/softusb/test/tb_navre.v b/cores/softusb/test/tb_navre.v index 5329fa5..45a70e6 100644 --- a/cores/softusb/test/tb_navre.v +++ b/cores/softusb/test/tb_navre.v @@ -97,6 +97,8 @@ softusb_navre #( .io_do(io_do), .io_di(io_di), + .irq(8'b0), + .dbg_pc() ); -- 1.7.2.5 _______________________________________________ http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org IRC: #milkymist@Freenode