stack-machine/cpu.v

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