// synthesis attribute slice_utilization_ratio of StackCPU is 50; module StackCPU (CLK_I, RST_I, IRQ_I, IACK_O, IADR_I, INST_ADR_O, INST_DAT_I, INST_CYC_O, INST_STB_O, INST_ACK_I, DATA_ADR_O, DATA_DAT_I, DATA_DAT_O, DATA_CYC_O, DATA_STB_O, DATA_WE_O, DATA_SEL_O, DATA_ACK_I); input CLK_I; input RST_I; input IRQ_I; output IACK_O; input [31:0] IADR_I; output [31:0] INST_ADR_O; input [7:0] INST_DAT_I; output INST_CYC_O; output INST_STB_O; input INST_ACK_I; output [31:2] DATA_ADR_O; input [31:0] DATA_DAT_I; output [31:0] DATA_DAT_O; output DATA_CYC_O; output DATA_STB_O; output DATA_WE_O; output [3:0] DATA_SEL_O; input DATA_ACK_I; reg [31:0] PC = 32'd0; /* address of current instr. */ reg [31:0] T = 32'd0; /* Top of (data) stack */ reg [31:0] N = 32'd0; /* Next of (data) stack */ reg [31:0] R = 32'd0; /* top of Return stack */ reg [8:0] SP = 9'd0; /* Stack Pointer */ reg [8:0] RP = 9'd0; /* Return stack Pointer */ wire [31:0] INST_ADR_O = PC; reg INST_CYC_O = 1'b1; reg INST_STB_O = 1'b1; wire [31:2] DATA_ADR_O = T[31:2]; reg [31:0] DATA_DAT_O; reg DATA_CYC_O = 1'b0; reg DATA_STB_O = 1'b0; reg DATA_WE_O = 1'b0; reg [3:0] DATA_SEL_O = 4'b0000; wire [31:0] STACK_M1_OUT; wire [31:0] STACK_M2_OUT; wire [31:0] RETURN_OUT; wire CMP_EQ = ~|N; wire CMP_LT = N[31]; wire CMP_GT = ~(CMP_EQ|CMP_LT); reg CMP_TRUE; StackRAM DataStack1 ( .CLKIN(CLK_I), .ADDR_A(SP), .WE_A(1'b1), .IN_A(N), .ADDR_B(SP - 9'd1), .OUT_B(STACK_M1_OUT) ); StackRAM DataStack2 ( .CLKIN(CLK_I), .ADDR_A(SP), .WE_A(1'b1), .IN_A(N), .ADDR_B(SP - 9'd2), .OUT_B(STACK_M2_OUT) ); StackRAM ReturnStack ( .CLKIN(CLK_I), .ADDR_A(RP), .WE_A(1'b1), .IN_A(R), .ADDR_B(RP - 9'd1), .OUT_B(RETURN_OUT) ); wire [5:0] NLZ_OUT; NLZ NLZ_inst ( .IN(T), .OUT(NLZ_OUT) ); reg DO_IRQ = 1'b0; wire IACK_O = IRQ_I & DO_IRQ; reg USE_SAVED_IR = 1'b0; reg [7:0] SAVED_IR; wire [7:0] IR = USE_SAVED_IR ? SAVED_IR : INST_DAT_I; wire IR_AVAIL = (USE_SAVED_IR | INST_ACK_I) & ~DO_IRQ; wire IS_DATA = (IR[7:2] == 6'b000110); reg USE_SAVED_DATA = 1'b0; reg [31:0] SAVED_DATA; wire [31:0] DATA_IN = USE_SAVED_DATA ? SAVED_DATA : DATA_DAT_I; wire DATA_ACKED = (USE_SAVED_DATA | DATA_ACK_I); reg [7:0] BYTE_IN; always @* begin casex (IR[3:1]) 3'b000: /* never */ CMP_TRUE <= 1'b0; 3'b001: /* equal */ CMP_TRUE <= CMP_EQ; 3'b010: /* less */ CMP_TRUE <= CMP_LT; 3'b011: /* greater */ CMP_TRUE <= CMP_GT; 3'b100: /* always */ CMP_TRUE <= 1'b1; 3'b101: /* !equal */ CMP_TRUE <= !CMP_EQ; 3'b110: /* !less */ CMP_TRUE <= !CMP_LT; 3'b111: /* !greater */ CMP_TRUE <= !CMP_GT; endcase DATA_CYC_O <= IR_AVAIL & IS_DATA & ~USE_SAVED_DATA; DATA_STB_O <= IR_AVAIL & IS_DATA & ~USE_SAVED_DATA; DATA_WE_O <= IR[1]; /* 1 if STORE */ casex ({IR[0], T[1:0]}) 3'b0XX: DATA_SEL_O <= 4'b1111; 3'b100: DATA_SEL_O <= 4'b1000; 3'b101: DATA_SEL_O <= 4'b0100; 3'b110: DATA_SEL_O <= 4'b0010; 3'b111: DATA_SEL_O <= 4'b0001; endcase casex ({IR[0], T[1:0]}) 3'b0XX: DATA_DAT_O <= N; 3'b100: DATA_DAT_O <= { N[7:0], 24'h000000 }; 3'b101: DATA_DAT_O <= { 8'h00, N[7:0], 16'h0000 }; 3'b110: DATA_DAT_O <= { 16'h0000, N[7:0], 8'h00 }; 3'b111: DATA_DAT_O <= { 24'h000000, N[7:0] }; endcase casex (T[1:0]) 2'b00: BYTE_IN <= DATA_IN[31:24]; 2'b01: BYTE_IN <= DATA_IN[23:16]; 2'b10: BYTE_IN <= DATA_IN[15: 8]; 2'b11: BYTE_IN <= DATA_IN[ 7: 0]; endcase end wire IS_MULT = (IR == 8'b00010111); reg [63:0] MULT_OUT; reg MULT_READY = 1'b0; wire DATA_WAIT = IS_DATA & ~DATA_ACKED; wire MULT_WAIT = IS_MULT & ~MULT_READY; wire OPCODE_COMPLETE = IR_AVAIL & ~DATA_WAIT & ~MULT_WAIT; always @(posedge CLK_I) begin MULT_OUT <= $unsigned(N) * $unsigned(T); if (RST_I) begin PC <= 32'd0; SP <= 9'd0; RP <= 9'd0; T <= 32'd0; N <= 32'd0; R <= 32'd0; USE_SAVED_IR <= 1'b0; USE_SAVED_DATA <= 1'b0; INST_CYC_O <= 1'b0; INST_STB_O <= 1'b0; MULT_READY <= 1'b0; DO_IRQ <= 1'b0; end else begin if (DO_IRQ) begin // Simulate a call to the address in IADR_I USE_SAVED_IR <= 1'b0; USE_SAVED_DATA <= 1'b0; INST_CYC_O <= 1'b1; INST_STB_O <= 1'b1; MULT_READY <= 1'b0; DO_IRQ <= 1'b0; SP <= SP; RP <= RP+1; T <= T; N <= N; R <= PC; PC <= IADR_I; end else if (OPCODE_COMPLETE) begin USE_SAVED_IR <= 1'b0; USE_SAVED_DATA <= 1'b0; INST_CYC_O <= ~IRQ_I; INST_STB_O <= ~IRQ_I; MULT_READY <= 1'b0; DO_IRQ <= IRQ_I; casex (IR) 8'b0000001X: /* RSHIFT */ SP <= SP-1; 8'b000001XX: /* ADD/SUB */ SP <= SP-1; 8'b00001XXX: /* LOGIC */ SP <= SP-1; 8'b0001001X: /* DUP */ SP <= SP+1; 8'b00010100: /* OVER */ SP <= SP+1; 8'b0001101X: /* STORE */ SP <= SP-2; 8'b00011101: /* POP */ SP <= SP+1; 8'b00011111: /* PUSH */ SP <= SP-1; 8'b001XXXXX: /* IMMED */ SP <= SP+1; 8'b01XXXXX0: /* JUMP/CALL */ SP <= SP-1; 8'b01XXXXX1: /* J/C DROP */ SP <= SP-2; 8'bXXXXXXXX: /* other */ SP <= SP; endcase casex (IR) 8'b00010101: /* RDUP */ RP <= RP+1; 8'b00010111: /* MULT */ RP <= RP; 8'b00011001: /* LOAD BYTE */ RP <= RP; 8'b00011011: /* STORE BYTE */ RP <= RP; 8'b00011100: /* RDROP */ RP <= RP-1; 8'b00011101: /* POP */ RP <= RP-1; 8'b00011110: /* PUSHPC */ RP <= RP+1; 8'b00011111: /* PUSH */ RP <= RP+1; 8'b001XXXXX: /* IMMED */ RP <= RP; 8'b010XXXXX: /* JUMP */ RP <= RP; 8'b011XXXXX: /* CALL */ RP <= CMP_TRUE ? (RP+1) : RP; 8'b1XXXXXXX: /* MERGE */ RP <= RP; 8'bXXXXXXX1: /* RETURN */ RP <= RP-1; 8'bXXXXXXXX: /* other */ RP <= RP; endcase casex (IR) 8'b0000000X: /* NOP */ T <= T; 8'b0000001X: /* RSHIFT */ T <= T[31] ? (N << (-T[5:0])) : (N >> T[5:0]); 8'b0000010X: /* ADD */ T <= N + T; 8'b0000011X: /* SUB */ T <= N - T; 8'b0000100X: /* NIP */ T <= T; 8'b0000101X: /* AND */ T <= N & T; 8'b0000110X: /* OR */ T <= N | T; 8'b0000111X: /* XOR */ T <= N ^ T; 8'b0001000X: /* SWAP */ T <= N; 8'b0001001X: /* DUP */ T <= T; 8'b00010100: /* OVER */ T <= N; 8'b00010101: /* RDUP */ T <= T; 8'b00010110: /* NLZ */ T <= NLZ_OUT; 8'b00010111: /* MULT */ T <= MULT_OUT[63:32]; 8'b00011000: /* LOAD */ T <= DATA_IN; 8'b00011001: /* LOAD BYTE */ T <= BYTE_IN; 8'b0001101X: /* STORE */ T <= STACK_M1_OUT; 8'b00011100: /* RDROP */ T <= T; 8'b00011101: /* POP */ T <= R; 8'b00011110: /* PUSHPC */ T <= T; 8'b00011111: /* PUSH */ T <= N; 8'b001XXXXX: /* IMMED */ T <= $signed(IR[4:0]); 8'b01XXXXX0: /* JUMP/CALL */ T <= N; 8'b01XXXXX1: /* J/C DROP */ T <= STACK_M1_OUT; 8'b1XXXXXXX: /* MERGE */ T <= (T<<7) | IR[6:0]; endcase casex (IR) 8'b0000000X: /* NOP */ N <= N; 8'b0000001X: /* RSHIFT */ N <= STACK_M1_OUT; 8'b0000010X: /* ADD */ N <= STACK_M1_OUT; 8'b0000011X: /* SUB */ N <= STACK_M1_OUT; 8'b0000100X: /* NIP */ N <= STACK_M1_OUT; 8'b0000101X: /* AND */ N <= STACK_M1_OUT; 8'b0000110X: /* OR */ N <= STACK_M1_OUT; 8'b0000111X: /* XOR */ N <= STACK_M1_OUT; 8'b0001000X: /* SWAP */ N <= T; 8'b0001001X: /* DUP */ N <= T; 8'b00010100: /* OVER */ N <= T; 8'b00010101: /* RDUP */ N <= N; 8'b00010110: /* NLZ */ N <= N; 8'b00010111: /* MULT */ N <= MULT_OUT[31:0]; 8'b0001100X: /* LOAD */ N <= N; 8'b0001101X: /* STORE */ N <= STACK_M2_OUT; 8'b00011100: /* RDROP */ N <= N; 8'b00011101: /* POP */ N <= T; 8'b00011110: /* PUSHPC */ N <= N; 8'b00011111: /* PUSH */ N <= STACK_M1_OUT; 8'b001XXXXX: /* IMMED */ N <= T; 8'b01XXXXX0: /* JUMP/CALL */ N <= STACK_M1_OUT; 8'b01XXXXX1: /* J/C DROP */ N <= STACK_M2_OUT; 8'b1XXXXXXX: /* MERGE */ N <= N; endcase casex (IR) 8'b00010101: /* RDUP */ R <= R; 8'b00010111: /* MULT */ R <= R; 8'b00011001: /* LOAD BYTE */ R <= R; 8'b00011011: /* STORE BYTE */ R <= R; 8'b00011100: /* RDROP */ R <= RETURN_OUT; 8'b00011101: /* POP */ R <= RETURN_OUT; 8'b00011110: /* PUSHPC */ R <= PC+1; 8'b00011111: /* PUSH */ R <= T; 8'b001XXXXX: /* IMMED */ R <= R; 8'b010XXXXX: /* JUMP */ R <= R; 8'b011XXXXX: /* CALL */ R <= CMP_TRUE ? (PC+1) : R; 8'b1XXXXXXX: /* MERGE */ R <= R; 8'bXXXXXXX1: /* RETURN */ R <= RETURN_OUT; 8'bXXXXXXXX: /* other */ R <= R; endcase casex (IR) 8'b00010101: /* RDUP */ PC <= PC + 1; 8'b00010111: /* MULT */ PC <= PC + 1; 8'b00011001: /* LOAD BYTE */ PC <= PC + 1; 8'b00011011: /* STORE BYTE */ PC <= PC + 1; 8'b00011101: /* POP */ PC <= PC + 1; 8'b00011111: /* PUSH */ PC <= PC + 1; 8'b001XXXXX: /* IMMED */ PC <= PC + 1; 8'b01X0XXXX: /* JUMP/CALL */ PC <= CMP_TRUE ? T : (PC+1); 8'b01X1XXXX: /* " REL */ PC <= CMP_TRUE ? ((PC+1)+T) : (PC+1); 8'b1XXXXXXX: /* MERGE */ PC <= PC + 1; 8'bXXXXXXX1: /* RETURN */ PC <= R; 8'bXXXXXXXX: /* other */ PC <= PC + 1; endcase end else begin INST_CYC_O <= ~IR_AVAIL; INST_STB_O <= ~IR_AVAIL; if (INST_ACK_I) begin SAVED_IR <= INST_DAT_I; USE_SAVED_IR <= 1'b1; end if (DATA_ACK_I) begin SAVED_DATA <= DATA_DAT_I; USE_SAVED_DATA <= 1'b1; end MULT_READY <= 1'b1; end end end endmodule