347 lines
11 KiB
Verilog
347 lines
11 KiB
Verilog
// 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
|