commit b8a73ab3f7e443e1a9816cfd07af331fd475faec
parent 36f098f12f112a9411017fae333e0b329ec2106f
Author: Brian Swetland <swetland@frotz.net>
Date: Tue, 29 Dec 2015 20:23:55 -0800
rework all sorts of things
Diffstat:
12 files changed, 467 insertions(+), 900 deletions(-)
diff --git a/Makefile b/Makefile
@@ -7,7 +7,7 @@ VERILATOR := VERILATOR_ROOT=/work/verilator /work/verilator/bin/verilator
VOPTS := --top-module testbench --Mdir out --exe ../src/testbench.cpp --cc -CFLAGS -DTRACE --trace
-all: out/Vtestbench out/a16
+all: out/Vtestbench out/a16 out/d16
out/Vtestbench: $(SRCS) src/testbench.cpp
@mkdir -p out
@@ -17,7 +17,7 @@ out/Vtestbench: $(SRCS) src/testbench.cpp
run: out/Vtestbench out/test.hex
./out/Vtestbench -trace out/trace.vcd -dump out/memory.bin -load out/test.hex
-out/test.hex: src/test.s out/a16
+out/test.hex: src/test.s out/a16 out/d16
out/a16 src/test.s out/test.hex
#out/test.hex: test.hex
@@ -25,11 +25,11 @@ out/test.hex: src/test.s out/a16
out/a16: src/a16.c src/d16.c
@mkdir -p out
- gcc -Wall -O1 -o out/a16 src/a16.c src/d16.c
+ gcc -g -Wall -O1 -o out/a16 src/a16.c src/d16.c
out/d16: src/d16.c
@mkdir -p out
- gcc -Wall -O1 -o out/d16 -DSTANDALONE=1 src/d16.c
+ gcc -g -Wall -O1 -o out/d16 -DSTANDALONE=1 src/d16.c
clean:
rm -rf out/
diff --git a/hdl/cpu/alu.v b/hdl/cpu/alu.v
@@ -3,77 +3,51 @@
`timescale 1ns / 1ps
-`define MINIMAL_ALU
-
-module alu(
+module alu #(
+ parameter DWIDTH = 16,
+ parameter SWIDTH = 4
+ )(
input [3:0]op,
input [DWIDTH-1:0]adata,
input [DWIDTH-1:0]bdata,
- output reg [DWIDTH-1:0]rdata,
- input [3:0]flags_i,
- output [3:0]flags_o
+ output reg[DWIDTH-1:0]rdata
);
-parameter DWIDTH = 16;
-parameter SWIDTH = 4;
-
-wire a_neg = adata[DWIDTH-1];
-wire b_neg = bdata[DWIDTH-1];
-wire r_neg = rdata[DWIDTH-1];
-
-wire N = r_neg;
-wire Z = (rdata == {DWIDTH{1'b0}});
-wire C = (a_neg & b_neg) || ((a_neg & b_neg) && !r_neg);
-wire V = !(a_neg ^ b_neg) && (a_neg ^ r_neg);
-
-`ifndef MINIMAL_ALU
-wire [DWIDTH-1:0]carry = {{(DWIDTH-1){1'b0}}, (flags_i[1] & op[3])};
-wire [DWIDTH-1:0]add = adata + bdata + carry;
-wire [DWIDTH-1:0]sub = adata - bdata - carry;
+`ifdef BIT_OPS
wire [DWIDTH-1:0]bits = (1 << bdata[SWIDTH-1:0]);
`endif
-reg nz;
-reg cv;
-
-`ifdef MINIMAL_ALU
-always @(*) begin
- case (op[2:0])
- 3'b000: begin nz = 1'b0; cv = 1'b0; rdata = bdata; end
- 3'b001: begin nz = 1'b1; cv = 1'b0; rdata = adata & bdata; end
- 3'b010: begin nz = 1'b1; cv = 1'b0; rdata = adata | bdata; end
- 3'b011: begin nz = 1'b1; cv = 1'b0; rdata = adata ^ bdata; end
- 3'b100: begin nz = 1'b1; cv = 1'b1; rdata = adata + bdata; end
- 3'b101: begin nz = 1'b1; cv = 1'b1; rdata = adata - bdata; end
- 3'b110: begin nz = 1'b1; cv = 1'b0; rdata = bdata << 1; end
- 3'b111: begin nz = 1'b1; cv = 1'b0; rdata = bdata >> 1; end
- endcase
-end
-`else
always @(*) begin
case (op)
- 4'b0000: begin nz = 1'b0; cv = 1'b0; rdata = bdata; end
- 4'b0001: begin nz = 1'b1; cv = 1'b0; rdata = adata & bdata; end
- 4'b0010: begin nz = 1'b1; cv = 1'b0; rdata = adata | bdata; end
- 4'b0011: begin nz = 1'b1; cv = 1'b0; rdata = adata ^ bdata; end
- 4'b0100: begin nz = 1'b1; cv = 1'b1; rdata = add; end
- 4'b0101: begin nz = 1'b1; cv = 1'b1; rdata = sub; end
- 4'b0110: begin nz = 1'b1; cv = 1'b0; rdata = bdata << 1; end
- 4'b0111: begin nz = 1'b1; cv = 1'b0; rdata = bdata >> 1; end
- 4'b1000: begin nz = 1'b1; cv = 1'b1; rdata = add; end // adc
- 4'b1001: begin nz = 1'b1; cv = 1'b1; rdata = sub; end // sbc
- 4'b1010: begin nz = 1'b1; cv = 1'b0; rdata = bdata << 4; end
- 4'b1011: begin nz = 1'b1; cv = 1'b0; rdata = bdata >> 4; end
- 4'b1100: begin nz = 1'b1; cv = 1'b0; rdata = adata | bits; end // bis
- 4'b1101: begin nz = 1'b1; cv = 1'b0; rdata = adata & (~bits); end // bic
- 4'b1110: begin nz = 1'b1; cv = 1'b0; rdata = adata & bits; end // tbs
- 4'b1111: begin nz = 1'b1; cv = 1'b0; rdata = adata * bdata; end
+ 4'b0000: rdata = bdata;
+ 4'b0001: rdata = adata & bdata;
+ 4'b0010: rdata = adata | bdata;
+ 4'b0011: rdata = adata ^ bdata;
+ 4'b0100: rdata = adata + bdata;
+ 4'b0101: rdata = adata - bdata;
+ 4'b0110: rdata = adata * bdata;
+ 4'b0111: rdata = { bdata[7:0], adata[7:0] };
+ 4'b1000: rdata = { {(DWIDTH-1){1'b0}}, adata < bdata };
+ 4'b1001: rdata = { {(DWIDTH-1){1'b0}}, adata <= bdata };
+`ifdef FULL_SHIFTER
+ 4'b1010: rdata = adata >> bdata[SWIDTH-1:0];
+ 4'b1011: rdata = adata << bdata[SWIDTH-1:0];
+`else
+ 4'b1010: rdata = { 1'b0, adata[DWIDTH-1:1] };
+ 4'b1011: rdata = { adata[DWIDTH-2:0], 1'b0 };
+`endif
+`ifdef BIT_OPS
+ 4'b1100: rdata = adata | bits;
+ 4'b1101: rdata = adata & (~bits);
+ 4'b1110: rdata = adata & bits;
+ 4'b1111: rdata = bits;
+`else
+ 4'b1100: rdata = {DWIDTH{1'bX}};
+ 4'b1101: rdata = {DWIDTH{1'bX}};
+ 4'b1110: rdata = {DWIDTH{1'bX}};
+ 4'b1111: rdata = {DWIDTH{1'bX}};
+`endif
endcase
end
-`endif
-assign flags_o[3] = nz ? N : flags_i[3];
-assign flags_o[2] = nz ? Z : flags_i[2];
-assign flags_o[1] = cv ? C : flags_i[1];
-assign flags_o[0] = cv ? V : flags_i[0];
endmodule
diff --git a/hdl/cpu/cpu.v b/hdl/cpu/cpu.v
@@ -3,7 +3,10 @@
`timescale 1ns / 1ps
-module cpu(
+module cpu #(
+ parameter RWIDTH = 16,
+ parameter SWIDTH = 4
+ )(
input clk,
output [15:0]mem_raddr_o,
input [15:0]mem_rdata_i,
@@ -13,106 +16,82 @@ module cpu(
output mem_rd_o
);
-parameter RWIDTH = 16;
-parameter SWIDTH = 4;
-
localparam AWIDTH = 16;
localparam DWIDTH = 16;
localparam IWIDTH = 16;
-localparam S_DECODE = 2'd0;
-localparam S_IMMEDIATE = 2'd1;
-localparam S_EXEC = 2'd2;
-localparam S_LOAD = 2'd3;
-
// control signals
reg do_fetch;
reg do_load;
reg do_store;
-reg do_wreg;
-reg do_load_adata;
-reg do_load_bdata;
-reg do_load_flags;
-reg do_load_wsel;
-reg [2:0]do_sel_bdata;
+wire do_wreg;
+reg [1:0]do_sel_bdata;
reg [1:0]do_sel_wsel;
reg [1:0]do_sel_branch;
-reg do_alu_op_mov; // override ALU opcode
-reg do_1mem_0alu; // select input to regs.wdata
-
-wire ir_cond_true;
+reg [1:0]do_sel_alu_op;
+reg do_exe_alu;
+reg do_exe_load;
+reg do_exe_branch;
-// processor registers (fetch unit)
+// processor registers
reg [AWIDTH-1:0]pc = 16'b0;
reg [15:0]ir = 16'b0;
-reg [15:0]ir_next;
reg ir_valid = 1'b0;
-reg ir_valid_next;
reg ir_loading = 1'b0;
+
+reg [15:0]ir_next;
+reg ir_valid_next;
reg ir_loading_next;
-// processor registers (main)
-reg [1:0]state = S_DECODE;
-reg [1:0]state_next;
-reg [3:0]flags = 4'b0;
+// registers that allow/disallow EXEC/LOAD and IMMEDIATE
+reg exe_alu = 1'b0;
+reg exe_load = 1'b0;
+reg exe_branch = 1'b0;
-// various registers loaded during DECODE for use in EXEC/LOAD/STORE
+// registers loaded during DECODE for use in EXEC/LOAD
reg [3:0]alu_op = 4'b0;
reg [RWIDTH-1:0]adata = 16'b0;
reg [RWIDTH-1:0]bdata = 16'b0;
reg [3:0]wsel = 4'b0;
// in/out of alu
-wire [RWIDTH-1:0]alu_adata = adata;
-wire [RWIDTH-1:0]alu_bdata = bdata;
wire [RWIDTH-1:0]alu_rdata;
-wire [3:0]alu_flags;
+
+// values computed
+wire [3:0]ir_asel = ir[7:4];
+wire [3:0]ir_bsel = ir[11:8];
+wire [RWIDTH-1:0]ir_imm_s4 = { {(RWIDTH-3) {ir[15]}}, ir[14:12] };
+wire [RWIDTH-1:0]ir_imm_s8 = { {(RWIDTH-7) {ir[15]}}, ir[14:8] };
+wire [RWIDTH-1:0]ir_imm_s12 = { {(RWIDTH-11){ir[15]}}, ir[14:4] };
// in/out of reg file
-wire [3:0]regs_asel = ir[11:8];
-wire [3:0]regs_bsel = ir[7:4];
-wire [RWIDTH-1:0]regs_wdata = do_1mem_0alu ? { {(RWIDTH-DWIDTH){1'b0}}, mem_rdata_i } : alu_rdata;
+wire [3:0]regs_asel = ir_asel;
+wire [3:0]regs_bsel = ir_bsel;
+wire [RWIDTH-1:0]regs_wdata = exe_load ? { {(RWIDTH-DWIDTH){1'b0}}, mem_rdata_i } : alu_rdata;
wire [RWIDTH-1:0]regs_adata;
wire [RWIDTH-1:0]regs_bdata;
-// values computed
-wire [RWIDTH-1:0]ir_imm_s4mem = { {(RWIDTH-4){ir[3]}}, ir[3:0] };
-wire [RWIDTH-1:0]ir_imm_s4alu = { {(RWIDTH-4){ir[7]}}, ir[7:4] };
-wire [RWIDTH-1:0]ir_imm_s8 = { {(RWIDTH-8){ir[7]}}, ir[7:0] };
-wire [RWIDTH-1:0]ir_imm_s12 = { {(RWIDTH-12){ir[11]}}, ir[11:0] };
+wire [RWIDTH-1:0]load_store_addr = regs_bdata + ir_imm_s4;
-wire [RWIDTH-1:0]load_store_addr = regs_bdata + ir_imm_s4mem;
+wire [AWIDTH-1:0]inst_addr = do_load_pc ? pc_next : pc;
-// for trace convenience
-`ifdef verilator
-wire [3:0]ir_opcode = ir[15:12];
-wire [3:0]ir_aluop = (ir[15:12] == 4'b0010) ? ir[7:4] : ir[3:0];
-`endif
+reg [AWIDTH-1:0]branch_target;
-wire [AWIDTH-1:0]inst_addr = do_load_pc ? pc_next : pc;
+localparam BR_REL_S8 = 2'b00; // PC + S8 a short branch
+localparam BR_REL_S12 = 2'b01; // PC + S12 a long branch
+localparam BR_ABS_RB = 2'b10; // RB an indirect branch
+
+wire [AWIDTH-1:0]branch_imm = do_sel_branch[0] ? ir_imm_s12 : ir_imm_s8;
+wire [AWIDTH-1:0]branch_target_next = do_sel_branch[1] ? regs_bdata : (pc + branch_imm);
// memory interface
assign mem_wr_o = do_store;
assign mem_rd_o = 1;
assign mem_raddr_o = do_load ? load_store_addr[AWIDTH-1:0] : inst_addr;
assign mem_waddr_o = load_store_addr[AWIDTH-1:0];
-assign mem_wdata_o = adata[AWIDTH-1:0];
+assign mem_wdata_o = regs_adata[AWIDTH-1:0];
-
-localparam BR_NONE = 2'b00; // PC + 1 the normal fetch
-localparam BR_REL_S8 = 2'b01; // PC + S8 a short branch
-localparam BR_REL_S12 = 2'b10; // PC + S12 a long branch
-localparam BR_ABS_REG = 2'b11; // [RB] an indirect branch
-
-reg [AWIDTH-1:0]pc_next;
-always @(*) begin
- case (do_sel_branch)
- BR_NONE: pc_next = pc + { {(AWIDTH-1){1'b0}}, 1'b1 };
- BR_REL_S8: pc_next = pc + ir_imm_s8;
- BR_REL_S12: pc_next = pc + ir_imm_s12;
- BR_ABS_REG: pc_next = regs_bdata;
- endcase
-end
+wire [AWIDTH-1:0]pc_next = exe_branch ? branch_target : (pc + 16'd1);
reg do_load_pc;
@@ -128,7 +107,7 @@ always @(*) begin
//XXX don't issue a read if we know it's useless?
ir_loading_next = ~do_load;
- if (do_sel_branch != BR_NONE) begin
+ if (exe_branch) begin
// branch is always highest priority
ir_valid_next = 1'b0;
do_load_pc = 1'b1;
@@ -145,37 +124,32 @@ always @(*) begin
// ir has been consumed if it was non-empty
ir_valid_next = 1'b0;
end
+ end else begin
+ // not loading
+ if (do_fetch) begin
+ ir_valid_next = 1'b0;
+ end
end
end
always @(posedge clk) begin
-/*
- if (cpu_reset) begin
- pc <= {AWIDTH{1'b0}};
- ir_valid <= 1'b0;
- ir_loading <= 1'b0;
- end else begin
-*/
- pc <= do_load_pc ? pc_next : pc;
- ir_valid <= ir_valid_next;
- ir_loading <= ir_loading_next;
-// end
+ pc <= do_load_pc ? pc_next : pc;
+ ir_valid <= ir_valid_next;
+ ir_loading <= ir_loading_next;
ir <= ir_next;
end
-localparam BDATA_RB = 3'b000;
-localparam BDATA_IR = 3'b001;
-localparam BDATA_PC = 3'b010;
-localparam BDATA_S4 = 3'b011;
-localparam BDATA_S8 = 3'b111;
-
+localparam BDATA_RB = 2'b00;
+localparam BDATA_PC = 2'b01;
+localparam BDATA_S4 = 2'b10;
+localparam BDATA_S8 = 2'b11;
reg [RWIDTH-1:0]bdata_mux;
always @(*) begin
- case (do_sel_bdata[1:0])
- 2'b00: bdata_mux = regs_bdata;
- 2'b01: bdata_mux = ir;
- 2'b10: bdata_mux = pc;
- 2'b11: bdata_mux = do_sel_bdata[2] ? ir_imm_s8 : ir_imm_s4alu;
+ case (do_sel_bdata)
+ BDATA_RB: bdata_mux = regs_bdata;
+ BDATA_PC: bdata_mux = pc;
+ BDATA_S4: bdata_mux = ir_imm_s4;
+ BDATA_S8: bdata_mux = ir_imm_s8;
endcase
end
@@ -183,177 +157,202 @@ localparam WSEL_RA = 2'b00;
localparam WSEL_RB = 2'b01;
localparam WSEL_OP = 2'b10;
localparam WSEL_LR = 2'b11;
-
reg [3:0]wsel_mux;
always @(*) begin
case (do_sel_wsel)
- WSEL_RA: wsel_mux = ir[11:8];
- WSEL_RB: wsel_mux = ir[7:4];
- WSEL_OP: wsel_mux = { 2'b0, ir[13:12] };
+ WSEL_RA: wsel_mux = ir[7:4];
+ WSEL_RB: wsel_mux = ir[11:8];
+ WSEL_OP: wsel_mux = { 2'b0, ir[1:0] };
WSEL_LR: wsel_mux = 4'd14;
endcase
end
+localparam ALU_MOV = 2'b00;
+localparam ALU_MHI = 2'b01;
+localparam ALU_FN_HI = 2'b10;
+localparam ALU_FN_LO = 2'b11;
+reg [3:0]alu_op_mux;
always @(*) begin
- state_next = state;
+ case (do_sel_alu_op)
+ ALU_MOV: alu_op_mux = 4'b0000;
+ ALU_MHI: alu_op_mux = 4'b0111;
+ ALU_FN_HI: alu_op_mux = ir[15:12];
+ ALU_FN_LO: alu_op_mux = ir[11:8];
+ endcase
+end
- // default actions
- do_fetch = 1'b0;
+wire regs_adata_zero = (regs_adata == 16'd0);
+
+assign do_wreg = exe_alu | exe_load;
+
+//`define WITH_BYPASS
+
+localparam USE_RA = 2'b10;
+localparam USE_RB = 2'b01;
+localparam USE_RA_RB = 2'b11;
+localparam USE_NONE = 2'b00;
+reg [1:0]using;
+wire conflict_a = (wsel == ir_asel) & using[1];
+wire conflict_b = (wsel == ir_bsel) & using[0];
+
+always @(*) begin
+ // decode stage
+ do_fetch = 1'b1;
do_load = 1'b0;
do_store = 1'b0;
- do_wreg = 1'b0;
- do_alu_op_mov = 1'b0;
- do_1mem_0alu = 1'b0;
- do_load_adata = 1'b0;
- do_load_bdata = 1'b0;
- do_load_flags = 1'b0;
- do_load_wsel = 1'b0;
- do_sel_branch = BR_NONE;
+ do_sel_branch = BR_ABS_RB;
do_sel_bdata = BDATA_RB;
do_sel_wsel = WSEL_RA;
+ do_sel_alu_op = ALU_MOV;
- case (state)
- S_IMMEDIATE: begin
- do_fetch = 1'b1;
- if (ir_valid) begin
- state_next = S_EXEC;
- do_sel_bdata = BDATA_IR;
- do_load_bdata = 1'b1;
- do_alu_op_mov = 1'b1;
- end
+ do_exe_alu = 1'b0;
+ do_exe_load = 1'b0;
+ do_exe_branch = 1'b0;
+
+ using = USE_RA_RB;
+
+ if (exe_branch) begin
+ do_fetch = 1'b0;
+ end
+`ifndef WITH_BYPASS
+ else if (exe_alu | exe_load) begin
+ do_fetch = ~(conflict_a | conflict_b);
end
- S_DECODE: begin
- do_fetch = 1'b1;
- state_next = S_EXEC;
+`endif
+
+ casez (ir[3:0])
+ 4'b0000: begin // mov Ra, imm
+ using = USE_NONE;
+ do_exe_alu = 1'b1;
+ do_sel_alu_op = ALU_MOV;
+ do_sel_wsel = WSEL_RA;
+ do_sel_bdata = BDATA_S8;
+ end
+ 4'b0001: begin // mhi Ra, imm
+ using = USE_NONE;
+ do_exe_alu = 1'b1;
+ do_sel_alu_op = ALU_MHI;
+ do_sel_wsel = WSEL_RA;
+ do_sel_bdata = BDATA_S8;
+ end
+ 4'b0010: begin // alu Ra, Ra, Rb
+ using = USE_RA_RB;
+ do_exe_alu = 1'b1;
+ do_sel_alu_op = ALU_FN_HI;
do_sel_wsel = WSEL_RA;
do_sel_bdata = BDATA_RB;
- do_load_adata = 1'b1;
- do_load_bdata = 1'b1;
- do_load_wsel = 1'b1;
- casez ({ir_valid, ir[15:12]})
- 5'b0????: begin
- // ir is invalid (wait state)
- // try again next cycle
- state_next = S_DECODE;
- end
- 5'b10000: begin // alu Ra, Ra, Rb
- state_next = S_EXEC;
- end
- 5'b10001: begin // mov immediate
- state_next = S_EXEC;
- do_alu_op_mov = 1'b1;
- do_sel_bdata = BDATA_S8;
- end
- 5'b10010: begin // alu Ra, Ra, imm4
- state_next = S_EXEC;
- do_sel_bdata = BDATA_S4;
- end
- 5'b10011: begin // alu Rb, Ra, imm16
- state_next = S_IMMEDIATE;
- do_sel_wsel = WSEL_RB;
- end
- 5'b101??: begin // alu Rd, Ra, Rb
- state_next = S_EXEC;
- do_sel_wsel = WSEL_OP;
- end
- 5'b11000: begin // lw Ra, [Rb, imm]
- state_next = S_LOAD;
- do_load = 1'b1;
- do_alu_op_mov = 1'b1;
- end
- 5'b11001: begin // sw Ra, [Rb, imm]
- state_next = S_DECODE;
- do_store = 1'b1;
- end
- 5'b11010: begin // bC imm8
- state_next = S_DECODE;
- if (ir_cond_true) begin
- do_sel_branch = BR_REL_S8;
- end
- end
- 5'b11011: begin // bC [Rb] / blC [Rb]
- state_next = S_DECODE;
- if (ir_cond_true) begin
- do_sel_branch = BR_ABS_REG;
- if (ir[3]) begin
- // arrange to write PC to LR
- state_next = S_EXEC;
- do_sel_bdata = BDATA_PC;
- do_alu_op_mov = 1'b1;
- do_sel_wsel = WSEL_LR;
- end
+ end
+ 4'b0011: begin // alu Ra, Ra, imm4
+ using = USE_RA;
+ do_exe_alu = 1'b1;
+ do_sel_alu_op = ALU_FN_LO;
+ do_sel_wsel = WSEL_RA;
+ do_sel_bdata = BDATA_S4;
+ end
+ 4'b01??: begin // alu Rd, Ra, Rb
+ using = USE_RA_RB;
+ do_exe_alu = 1'b1;
+ do_sel_alu_op = ALU_FN_HI;
+ do_sel_wsel = WSEL_OP;
+ do_sel_bdata = BDATA_RB;
+ end
+ 4'b1000: begin // lw Ra, [Rb, imm]
+ using = USE_RB;
+ do_exe_load = 1'b1;
+ do_load = ir_valid;
+ end
+ 4'b1001: begin // sw Ra, [Rb, imm]
+ using = USE_RA_RB;
+ do_store = ir_valid;
+ end
+ 4'b1010: begin // bnz Ra, rel8
+ using = USE_RA;
+ do_exe_branch = ~regs_adata_zero;
+ do_sel_branch = BR_REL_S8;
+ end
+ 4'b1011: begin // bz Ra, rel8
+ using = USE_RA;
+ do_exe_branch = regs_adata_zero;
+ do_sel_branch = BR_REL_S8;
+ end
+ 4'b1100: begin // b rel12
+ using = USE_NONE;
+ do_sel_branch = BR_REL_S12;
+ end
+ 4'b1101: begin // bl rel12
+ using = USE_NONE;
+ do_exe_alu = 1'b1;
+ do_exe_branch = 1'b1;
+ do_sel_branch = BR_REL_S12;
+ do_sel_alu_op = ALU_MOV;
+ do_sel_bdata = BDATA_PC;
+ do_sel_wsel = WSEL_LR;
+ end
+ 4'b1110: begin
+ if (ir[15:13] == 3'b000) begin // b Rb / bl Rb
+ using = USE_RB;
+ do_exe_branch = 1'b1;
+ do_sel_branch = BR_ABS_RB;
+ if (ir[12]) begin
+ do_exe_alu = 1'b1;
+ do_sel_alu_op = ALU_MOV;
+ do_sel_bdata = BDATA_PC;
+ do_sel_wsel = WSEL_LR;
end
end
- 5'b11100: begin // b rel12
- do_sel_branch = BR_REL_S12;
- state_next = S_DECODE;
- end
- 5'b11101: begin // bl rel12
- do_sel_branch = BR_REL_S12;
- // arrange to write PC to LR
- state_next = S_EXEC;
- do_sel_bdata = BDATA_PC;
- do_alu_op_mov = 1'b1;
- do_sel_wsel = WSEL_LR;
- end
- default: begin
- // treat undefined as NOP
- state_next = S_DECODE;
- end
- endcase
- end
- S_EXEC: begin
- state_next = S_DECODE;
- do_load_flags = 1'b1;
- do_wreg = 1'b1;
- do_1mem_0alu = 1'b0;
end
- S_LOAD: begin
- state_next = S_DECODE;
- do_wreg = 1'b1;
- do_1mem_0alu = 1'b1;
+ default: begin
+ // treat undefined as NOP
end
endcase
end
always @(posedge clk) begin
- state <= state_next;
- flags <= do_load_flags ? alu_flags : flags;
- alu_op <= do_alu_op_mov ? 4'b0000 : ir[3:0];
- adata <= do_load_adata ? regs_adata : adata;
- bdata <= do_load_bdata ? bdata_mux : bdata;
- wsel <= do_load_wsel ? wsel_mux : wsel;
+ alu_op <= alu_op_mux;
+ adata <= regs_adata;
+ bdata <= bdata_mux;
+ wsel <= wsel_mux;
+ exe_alu <= ir_valid & do_fetch & do_exe_alu;
+ exe_load <= ir_valid & do_fetch & do_exe_load;
+ exe_branch <= ir_valid & do_fetch & do_exe_branch;
+ branch_target <= branch_target_next;
+
end
+wire [DWIDTH-1:0]raw_regs_adata;
+wire [DWIDTH-1:0]raw_regs_bdata;
+
regfile #(
.DWIDTH(RWIDTH)
)regs(
.clk(clk),
.asel(regs_asel),
.bsel(regs_bsel),
- .adata(regs_adata),
- .bdata(regs_bdata),
+ .adata(raw_regs_adata),
+ .bdata(raw_regs_bdata),
.wsel(wsel),
.wdata(regs_wdata),
.wreg(do_wreg)
);
+`ifdef WITH_BYPASS
+wire bypass_a = do_wreg & (wsel == ir_asel) & (~exe_load);
+wire bypass_b = do_wreg & (wsel == ir_bsel) & (~exe_load);
+assign regs_adata = bypass_a ? regs_wdata : raw_regs_adata;
+assign regs_bdata = bypass_b ? regs_wdata : raw_regs_bdata;
+`else
+assign regs_adata = raw_regs_adata;
+assign regs_bdata = raw_regs_bdata;
+`endif
+
alu #(
.DWIDTH(RWIDTH),
.SWIDTH(SWIDTH)
)alu0(
.op(alu_op),
- .adata(alu_adata),
- .bdata(alu_bdata),
- .rdata(alu_rdata),
- .flags_o(alu_flags),
- .flags_i(flags)
- );
-
-check_cond check(
- .flags_i(flags),
- .cond_i(ir[11:8]),
- .is_true_o(ir_cond_true)
+ .adata(adata),
+ .bdata(bdata),
+ .rdata(alu_rdata)
);
endmodule
diff --git a/hdl/cpu/cpu_pipelined.v b/hdl/cpu/cpu_pipelined.v
@@ -1,378 +0,0 @@
-// Copyright 2015, Brian Swetland <swetland@frotz.net>
-// Licensed under the Apache License, Version 2.0.
-
-`timescale 1ns / 1ps
-
-module cpu(
- input clk,
- output [15:0]mem_raddr_o,
- input [15:0]mem_rdata_i,
- output [15:0]mem_waddr_o,
- output [15:0]mem_wdata_o,
- output mem_wr_o,
- output mem_rd_o
- );
-
-parameter RWIDTH = 16;
-parameter SWIDTH = 4;
-
-localparam AWIDTH = 16;
-localparam DWIDTH = 16;
-localparam IWIDTH = 16;
-
-// control signals
-reg do_fetch;
-reg do_load;
-reg do_store;
-reg do_wreg;
-reg do_load_adata;
-reg do_load_bdata;
-reg do_load_flags;
-reg do_load_wsel;
-reg do_load_alu_op;
-reg [2:0]do_sel_bdata;
-reg [1:0]do_sel_wsel;
-reg [1:0]do_sel_branch;
-reg do_alu_op_mov; // override ALU opcode
-reg do_1mem_0alu; // select input to regs.wdata
-reg do_exe_alu;
-reg do_exe_load;
-reg do_dec_imm;
-
-wire ir_cond_true;
-
-// processor registers
-reg [AWIDTH-1:0]pc = 16'b0;
-reg [3:0]flags = 4'b0;
-reg [15:0]ir = 16'b0;
-reg ir_valid = 1'b0;
-reg ir_loading = 1'b0;
-
-reg [15:0]ir_next;
-reg ir_valid_next;
-reg ir_loading_next;
-
-// registers that allow/disallow EXEC/LOAD and IMMEDIATE
-reg exe_alu = 1'b0;
-reg exe_load = 1'b0;
-reg dec_imm = 1'b0;
-
-// registers loaded during DECODE for use in EXEC/LOAD
-reg [3:0]alu_op = 4'b0;
-reg [RWIDTH-1:0]adata = 16'b0;
-reg [RWIDTH-1:0]bdata = 16'b0;
-reg [3:0]wsel = 4'b0;
-
-// in/out of alu
-wire [RWIDTH-1:0]alu_adata = adata;
-wire [RWIDTH-1:0]alu_bdata = bdata;
-wire [RWIDTH-1:0]alu_rdata;
-wire [3:0]alu_flags;
-
-// in/out of reg file
-wire [3:0]regs_asel = ir[11:8];
-wire [3:0]regs_bsel = ir[7:4];
-wire [RWIDTH-1:0]regs_wdata = do_1mem_0alu ? { {(RWIDTH-DWIDTH){1'b0}}, mem_rdata_i } : alu_rdata;
-wire [RWIDTH-1:0]regs_adata;
-wire [RWIDTH-1:0]regs_bdata;
-
-// values computed
-wire [RWIDTH-1:0]ir_imm_s4mem = { {(RWIDTH-4){ir[3]}}, ir[3:0] };
-wire [RWIDTH-1:0]ir_imm_s4alu = { {(RWIDTH-4){ir[7]}}, ir[7:4] };
-wire [RWIDTH-1:0]ir_imm_s8 = { {(RWIDTH-8){ir[7]}}, ir[7:0] };
-wire [RWIDTH-1:0]ir_imm_s12 = { {(RWIDTH-12){ir[11]}}, ir[11:0] };
-
-wire [RWIDTH-1:0]load_store_addr = regs_bdata + ir_imm_s4mem;
-
-wire [AWIDTH-1:0]inst_addr = do_load_pc ? pc_next : pc;
-
-// memory interface
-assign mem_wr_o = do_store;
-assign mem_rd_o = 1;
-assign mem_raddr_o = do_load ? load_store_addr[AWIDTH-1:0] : inst_addr;
-assign mem_waddr_o = load_store_addr[AWIDTH-1:0];
-assign mem_wdata_o = regs_adata[AWIDTH-1:0];
-
-
-localparam BR_NONE = 2'b00; // PC + 1 the normal fetch
-localparam BR_REL_S8 = 2'b01; // PC + S8 a short branch
-localparam BR_REL_S12 = 2'b10; // PC + S12 a long branch
-localparam BR_ABS_REG = 2'b11; // [RB] an indirect branch
-
-reg [AWIDTH-1:0]pc_next;
-always @(*) begin
- case (do_sel_branch)
- BR_NONE: pc_next = pc + { {(AWIDTH-1){1'b0}}, 1'b1 };
- BR_REL_S8: pc_next = pc + ir_imm_s8;
- BR_REL_S12: pc_next = pc + ir_imm_s12;
- BR_ABS_REG: pc_next = regs_bdata;
- endcase
-end
-
-reg do_load_pc;
-
-// lw r3, [r0]
-// mov r0, #0xDEAD
-always @(*) begin
- ir_next = ir;
- ir_valid_next = ir_valid;
- ir_loading_next = ir_loading;
-
- do_load_pc = 1'b0;
-
- // we try to read an instruction every cycle
- // unless we're pre-empted by a data load
- //XXX don't issue a read if we know it's useless?
- ir_loading_next = ~do_load;
-
- if (do_sel_branch != BR_NONE) begin
- // branch is always highest priority
- ir_valid_next = 1'b0;
- do_load_pc = 1'b1;
- end else if (ir_loading) begin
- // we've read an instruction
- if ((~ir_valid) | do_fetch) begin
- // ir was empty or is being consumed
- // fill it with the just-read instruction
- // and advance the pc
- ir_next = mem_rdata_i;
- ir_valid_next = 1'b1;
- do_load_pc = 1'b1;
- end else if (do_fetch) begin
- // ir has been consumed if it was non-empty
- ir_valid_next = 1'b0;
- end
- end else begin
- // not loading
- if (do_fetch) begin
- ir_valid_next = 1'b0;
- end
- end
-end
-
-always @(posedge clk) begin
-/*
- if (cpu_reset) begin
- pc <= {AWIDTH{1'b0}};
- ir_valid <= 1'b0;
- ir_loading <= 1'b0;
- end else begin
-*/
- pc <= do_load_pc ? pc_next : pc;
- ir_valid <= ir_valid_next;
- ir_loading <= ir_loading_next;
-// end
- ir <= ir_next;
-end
-
-localparam BDATA_RB = 3'b000;
-localparam BDATA_IR = 3'b001;
-localparam BDATA_PC = 3'b010;
-localparam BDATA_S4 = 3'b011;
-localparam BDATA_S8 = 3'b111;
-
-reg [RWIDTH-1:0]bdata_mux;
-always @(*) begin
- case (do_sel_bdata[1:0])
- 2'b00: bdata_mux = regs_bdata;
- 2'b01: bdata_mux = ir;
- 2'b10: bdata_mux = pc;
- 2'b11: bdata_mux = do_sel_bdata[2] ? ir_imm_s8 : ir_imm_s4alu;
- endcase
-end
-
-localparam WSEL_RA = 2'b00;
-localparam WSEL_RB = 2'b01;
-localparam WSEL_OP = 2'b10;
-localparam WSEL_LR = 2'b11;
-
-reg [3:0]wsel_mux;
-always @(*) begin
- case (do_sel_wsel)
- WSEL_RA: wsel_mux = ir[11:8];
- WSEL_RB: wsel_mux = ir[7:4];
- WSEL_OP: wsel_mux = { 2'b0, ir[13:12] };
- WSEL_LR: wsel_mux = 4'd14;
- endcase
-end
-
-always @(*) begin
- // decode stage
- do_fetch = 1'b1;
- do_load = 1'b0;
- do_store = 1'b0;
- do_alu_op_mov = 1'b0;
- do_load_adata = 1'b1;
- do_load_bdata = 1'b1;
- do_load_wsel = 1'b1;
- do_load_alu_op = 1'b1;
- do_sel_branch = BR_NONE;
- do_sel_bdata = BDATA_RB;
- do_sel_wsel = WSEL_RA;
-
- do_dec_imm = 1'b0;
- do_exe_alu = 1'b0;
- do_exe_load = 1'b0;
-
- // load/exec stage
- do_wreg = 1'b0;
- do_1mem_0alu = 1'b0;
- do_load_flags = 1'b0;
-
- casez ({ir_valid, dec_imm, ir[15:12]})
- 6'b0?????: begin
- // ir is invalid (wait state)
- // try again next cycle
- do_dec_imm = dec_imm;
- // don't modify any state
- // otherwise we could break wide ops
- do_load_adata = 1'b0;
- do_load_bdata = 1'b0;
- do_load_wsel = 1'b0;
- do_load_alu_op = 1'b0;
- end
- 6'b11????: begin // alu Rb, Ra, imm16 (second half)
- do_exe_alu = 1'b1;
- do_sel_bdata = BDATA_IR;
- do_load_bdata = 1'b1;
- // some data we carry from first half of instr
- do_load_adata = 1'b0;
- do_load_wsel = 1'b0;
- do_load_alu_op = 1'b0;
- end
- 6'b100000: begin // alu Ra, Ra, Rb
- do_exe_alu = 1'b1;
- end
- 6'b100001: begin // mov immediate
- do_exe_alu = 1'b1;
- do_alu_op_mov = 1'b1;
- do_sel_bdata = BDATA_S8;
- end
- 6'b100010: begin // alu Ra, Ra, imm4
- do_exe_alu = 1'b1;
- do_sel_bdata = BDATA_S4;
- end
- 6'b100011: begin // alu Rb, Ra, imm16 (first half)
- do_dec_imm = 1'b1;
- do_sel_wsel = WSEL_RB;
- end
- 6'b1001??: begin // alu Rd, Ra, Rb
- do_exe_alu = 1'b1;
- do_sel_wsel = WSEL_OP;
- end
- 6'b101000: begin // lw Ra, [Rb, imm]
- do_exe_load = 1'b1;
- do_load = 1'b1;
- do_alu_op_mov = 1'b1;
- end
- 6'b101001: begin // sw Ra, [Rb, imm]
- do_store = 1'b1;
- end
- 6'b101010: begin // bC imm8
- if (ir_cond_true) begin
- do_sel_branch = BR_REL_S8;
- end
- end
- 6'b101011: begin // bC [Rb] / blC [Rb]
- if (ir_cond_true) begin
- do_sel_branch = BR_ABS_REG;
- if (ir[3]) begin
- // arrange to write PC to LR
- do_exe_alu = 1'b1;
- do_sel_bdata = BDATA_PC;
- do_alu_op_mov = 1'b1;
- do_sel_wsel = WSEL_LR;
- end
- end
- end
- 6'b101100: begin // b rel12
- do_sel_branch = BR_REL_S12;
- end
- 6'b101101: begin // bl rel12
- do_sel_branch = BR_REL_S12;
- // arrange to write PC to LR
- do_exe_alu = 1'b1;
- do_sel_bdata = BDATA_PC;
- do_alu_op_mov = 1'b1;
- do_sel_wsel = WSEL_LR;
- end
- default: begin
- // treat undefined as NOP
- end
- endcase
-
- // ALU / LOAD stage
- if (exe_load) begin
- do_wreg = 1'b1;
- do_1mem_0alu = 1'b1;
- end else if (exe_alu) begin
- do_wreg = 1'b1;
- do_load_flags = 1'b1;
- do_1mem_0alu = 1'b0;
- end
-end
-
-always @(posedge clk) begin
- flags <= do_load_flags ? alu_flags : flags;
- alu_op <= do_load_alu_op ? (do_alu_op_mov ? 4'b0000 : ir[3:0]) : alu_op;
- adata <= do_load_adata ? regs_adata : adata;
- bdata <= do_load_bdata ? bdata_mux : bdata;
- wsel <= do_load_wsel ? wsel_mux : wsel;
- exe_alu <= do_exe_alu;
- exe_load <= do_exe_load;
- dec_imm <= do_dec_imm;
-end
-
-wire [DWIDTH-1:0]raw_regs_adata;
-wire [DWIDTH-1:0]raw_regs_bdata;
-
-regfile #(
- .DWIDTH(RWIDTH)
- )regs(
- .clk(clk),
- .asel(regs_asel),
- .bsel(regs_bsel),
- .adata(raw_regs_adata),
- .bdata(raw_regs_bdata),
- .wsel(wsel),
- .wdata(regs_wdata),
- .wreg(do_wreg)
- );
-
-`define BYPASS
-`ifdef BYPASS
-wire bypass_a = do_wreg & (wsel == ir[11:8]);
-wire bypass_b = do_wreg & (wsel == ir[7:4]);
-assign regs_adata = bypass_a ? regs_wdata : raw_regs_adata;
-assign regs_bdata = bypass_b ? regs_wdata : raw_regs_bdata;
-`else
-assign regs_adata = raw_regs_adata;
-assign regs_bdata = raw_regs_bdata;
-`endif
-
-alu #(
- .DWIDTH(RWIDTH),
- .SWIDTH(SWIDTH)
- )alu0(
- .op(alu_op),
- .adata(alu_adata),
- .bdata(alu_bdata),
- .rdata(alu_rdata),
- .flags_o(alu_flags),
- .flags_i(flags)
- );
-
-// when ALU is writing flags (do_load_flags),
-// use the newly written value instead of the register
-check_cond check(
-`ifdef BYPASS
- .flags_i(do_load_flags ? alu_flags : flags),
-`else
- .flags_i(flags),
-`endif
- .cond_i(ir[11:8]),
- .is_true_o(ir_cond_true)
- );
-
-endmodule
-
diff --git a/hdl/cpu/isa.txt b/hdl/cpu/isa.txt
@@ -1,33 +1,36 @@
-0000 aaaa bbbb ffff alu Ra, Ra, Rb
-0001 aaaa iiii iiii mov Ra, si8
-0010 aaaa iiii ffff alu Ra, Ra, si4
-0011 aaaa bbbb ffff alu Rb, Ra, si16 (fetches imm after)
-0100 aaaa bbbb ffff alu R0, Ra, Rb
-0101 aaaa bbbb ffff alu R1, Ra, Rb
-0110 aaaa bbbb ffff alu R2, Ra, Rb
-0111 aaaa bbbb ffff alu R3, Ra, Rb
+basic instruction set
+---------------------
+iiii iiii aaaa 0000 mov Ra, s8
+iiii iiii aaaa 0001 mhi Ra, s8
+ffff bbbb aaaa 0010 alu Ra, Ra, Rb
+iiii ffff aaaa 0011 alu Ra, Ra, s4
+ffff bbbb aaaa 01cc alu Rc, Ra, Rb
+iiii bbbb aaaa 1000 lw Ra, [Rb, si4]
+iiii bbbb aaaa 1001 sw Ra, [Rb, si4]
+iiii iiii aaaa 1010 bnz Ra, rel8
+iiii iiii aaaa 1011 bz Ra, rel8
+iiii iiii iiii 1100 b rel12
+iiii iiii iiii 1101 bl rel12
+0000 bbbb xxxx 1110 b Rb
+0001 bbbb xxxx 1110 bl Rb
+0010 xxxx xxxx 1110 nop
-1000 aaaa bbbb iiii lw Ra, [Rb, si4]
-1001 aaaa bbbb iiii sw Ra, [Rb, si4]
-1010 cccc iiii iiii bC rel8
-1010 1111 xxxx xxxx ? undefined
-1011 cccc bbbb 0000 bC Rb
-1011 cccc bbbb 1000 blC Rb
-1011 1111 bbbb 0000 * iret
-1011 1111 xxxx 1000 ? nop
-1011 xxxx xxxx 0001 ? undefined
-1011 nnnn bbbb 0010 * mov Rb, Sn
-1011 nnnn bbbb 0011 * mov Sn, Rb
-1011 nnnn nnnn 0100 * set <flags> (sysflags |= n)
-1011 nnnn nnnn 0101 * clr <flags> (sysflags &= n)
-1011 nnnn nnnn 0110 * syscall n
-1011 nnnn nnnn 0111 break n
-1100 iiii iiii iiii b pc + si12
-1101 iiii iiii iiii bl pc + si12
-1110 aaaa bbbb 0000 * mov Ra', Rb
-1110 aaaa bbbb 0001 * mov Ra, Rb'
-1111 xxxx xxxx xxxx ? undefined
+extended instruction set
+------------------------
+0011 xxxx xxxx 1110 iret
+0100 nnnn aaaa 1110 mov Ra, Sn
+0101 nnnn aaaa 1110 mov Sn, Ra
+0110 aaaa bbbb 1110 mov Ra', Rb
+0111 aaaa bbbb 1110 mov Ra, Rb'
+1xxx xxxx xxxx 1110 undefined
+nnnn nnnn 0000 1111 syscall n
+nnnn nnnn 0001 1111 break;
+nnnn nnnn 0010 1111 set <flags>
+nnnn nnnn 0011 1111 clr <flags>
+xxxx xxxx 0011 1111 undefined
+xxxx xxxx 01xx 1111 undefined
+xxxx xxxx 1xxx 1111 undefined
alu opcodes
-----------
@@ -37,19 +40,16 @@ alu opcodes
0011 xor r = a ^ b
0100 add r = a + b
0101 sub r = a - b
-0110 >>1 r = b >> 1
-0111 <<1 r = b << 1
-
-1000 adc * r = a + b + c
-1001 sbc * r = a - b - c
-1010 >>4 * r = b >> 4
-1011 <<4 * r = b << 4
-1100 bis * r = a | (1 << b[3:0])
-1101 bic * r = a & (~(1 << b[3:0]))
-1110 tbs * r = a & (1 << b[3:0])
-1111 mul * r = a * b
-
-* = only in "large" version of cpu
+0110 mul r = a * b
+0111 mhi r = (b << 8) | (a & 0xFF)
+1000 slt r = a < b
+1001 sle r = a <= b
+1010 shr r = a >> 1
+1011 shl r = a << 1
+1100 bis r = a | (1 << b[3:0])
+1101 bic r = a & (~(1 << b[3:0]))
+1110 tbs r = a & (1 << b[3:0])
+1111 bit r = 1 << b[3:0]
r0 - r12 general use
r13 stack (by convention only)
diff --git a/hdl/cpu/regfile.v b/hdl/cpu/regfile.v
@@ -9,7 +9,10 @@
// enough" behavior (since the path from regfile -> exec stage
// register is pretty short)
-module regfile(
+module regfile #(
+ parameter AWIDTH = 4,
+ parameter DWIDTH = 16
+ )(
input clk,
input [AWIDTH-1:0]asel,
input [AWIDTH-1:0]bsel,
@@ -20,23 +23,26 @@ module regfile(
input [DWIDTH-1:0]wdata
);
-parameter AWIDTH = 4;
-parameter DWIDTH = 16;
-
reg [DWIDTH-1:0] R[0:((1<<AWIDTH)-1)];
always @(posedge clk) begin
if (wreg)
R[wsel] <= wdata;
+// adata <= R[asel];
+// bdata <= R[bsel];
end
+assign adata = R[asel];
+assign bdata = R[bsel];
+/*
`ifdef NEGSYNC
always @(negedge clk) begin
adata = R[asel];
bdata = R[bsel];
end
`else
-assign adata = R[asel];
-assign bdata = R[bsel];
+`ifndef SYNC
+`endif
`endif
+*/
endmodule
diff --git a/src/a16.c b/src/a16.c
@@ -30,16 +30,19 @@ void die(const char *fmt, ...) {
}
int is_signed4(unsigned n) {
+ if (n & 0x8000) n |= 0xFFFF0000;
if (n <= 0x7) return 1;
if ((n & 0xFFFFFFF8) == 0xFFFFFFF8) return 1;
return 0;
}
int is_signed8(unsigned n) {
+ if (n & 0x8000) n |= 0xFFFF0000;
if (n <= 0xFF) return 1;
if ((n & 0xFFFFFF80) == 0xFFFFFF80) return 1;
return 0;
}
int is_signed12(unsigned n) {
+ if (n & 0x8000) n |= 0xFFFF0000;
if (n <= 0x7FF) return 1;
if ((n & 0xFFFFF800) == 0xFFFFF800) return 1;
return 0;
@@ -81,12 +84,12 @@ void fixup_branch(const char *name, int addr, int btarget, int type) {
case TYPE_PCREL_S8:
n = btarget - addr - 1;
if (!is_signed8(n)) break;
- rom[addr] = (rom[addr] & 0xFF00) | (n & 0x00FF);
+ rom[addr] = (rom[addr] & 0x00FF) | ((n & 0x00FF) << 8);
return;
case TYPE_PCREL_S12:
n = btarget - addr - 1;
if (!is_signed12(n)) break;
- rom[addr] = (rom[addr] & 0xF000) | (n & 0x0FFF);
+ rom[addr] = (rom[addr] & 0x000F) | ((n & 0x0FFF) << 4);
return;
case TYPE_ABS_U16:
rom[addr] = btarget;
@@ -167,7 +170,7 @@ void checklabels(void) {
}
}
-int disassemble(char *buf, unsigned pc, unsigned instr, unsigned next_instr);
+void disassemble(char *buf, unsigned pc, unsigned instr);
void emit(unsigned instr) {
rom[PC++] = instr;
@@ -177,19 +180,11 @@ void save(const char *fn) {
const char *name;
unsigned n;
char dis[128];
- int next_is_imm = 0;
FILE *fp = fopen(fn, "w");
if (!fp) die("cannot write to '%s'", fn);
for (n = 0; n < PC; n++) {
- if (next_is_imm) {
- next_is_imm = 0;
- fprintf(fp, "%04x // %04x:\n", rom[n], n);
- continue;
- }
- if (disassemble(dis, n, rom[n], rom[n+1]) == 2) {
- next_is_imm = 1;
- }
+ disassemble(dis, n, rom[n]);
name = getlabel(n);
if (name) {
fprintf(fp, "%04x // %04x: %-25s <- %s\n", rom[n], n, dis, name);
@@ -207,12 +202,8 @@ enum tokens {
tCOMMA, tCOLON, tOBRACK, tCBRACK, tDOT, tHASH, tSTRING, tNUMBER,
tMOV, tAND, tORR, tXOR, tADD, tSUB, tSHR, tSHL,
// tADC, tSBC, tSR4, tSL4, tBIS, tBIC, tTBS, tMUL,
- tLW, tSW, tNOP, tNOT,
- tBEQ, tBNE, tBCS, tBCC, tBMI, tBPL, tBVS, tBVC,
- tBHI, tBLS, tBGE, tBLT, tBGT, tBLE, tB, tBNV,
- tBLEQ, tBLNE, tBLCS, tBLCC, tBLMI, tBLPL, tBLVS, tBLVC,
- tBLHI, tBLLS, tBLGE, tBLLT, tBLGT, tBLLE, tBL, tBLNV,
- tBLZ, tBLNZ, tBZ, tBNZ,
+ tLW, tSW, tNOP, tNOT, tMHI,
+ tB, tBL, tBZ, tBNZ,
tR0, tR1, tR2, tR3, tR4, tR5, tR6, tR7,
rR8, rR9, rR10, rR11, rR12, tR13, tR14, tR15,
tSP, tLR,
@@ -225,12 +216,8 @@ char *tnames[] = {
",", ":", "[", "]", ".", "#", "<STRING>", "<NUMBER>",
"MOV", "AND", "ORR", "XOR", "ADD", "SUB", "SHR", "SHL",
// "ADC", "SBC", "SR4", "SL4", "BIS", "BIC", "TBS", "MUL",
- "LW", "SW", "NOP", "NOT",
- "BEQ", "BNE", "BCS", "BCC", "BMI", "BPL", "BVS", "BVC",
- "BHI", "BLS", "BGE", "BLT", "BGT", "BLE", "B", "BNV",
- "BLEQ", "BLNE", "BLCS", "BLCC", "BLMI", "BLPL", "BLVS", "BLVC",
- "BLHI", "BLLS", "BLGE", "BLLT", "BLGT", "BLLE", "BL", "BLNV",
- "BLZ", "BLNZ", "BZ", "BNZ",
+ "LW", "SW", "NOP", "NOT", "MHI",
+ "B", "BL", "BZ", "BNZ",
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
"SP", "LR",
@@ -241,44 +228,6 @@ char *tnames[] = {
#define LAST_ALU_OP tSHL
#define FIRST_REGISTER tR0
#define LAST_REGISTER tLR
-#define FIRST_BR_OP tBEQ
-#define LAST_BR_OP tBNZ
-
-int is_branch(unsigned tok) {
- return ((tok >= FIRST_BR_OP) && (tok <= LAST_BR_OP));
-}
-
-#define COND_EQ 0
-#define COND_NE 1
-#define COND_AL 14
-#define COND_NV 15
-
-unsigned to_cc(unsigned tok) {
- switch (tok) {
- case tBNZ: case tBLNZ:
- return COND_NE;
- case tB: case tBL:
- return COND_AL;
- default:
- return (tok - FIRST_BR_OP) & 15;
- }
-}
-
-int is_branch_link(unsigned tok) {
- return ((tok >= tBLEQ) && (tok <= tBLNZ));
-}
-
-int is_conditional(unsigned tok) {
- if (is_branch(tok)) {
- if ((tok == tB) | (tok == tBL)) {
- return 0;
- } else {
- return 1;
- }
- } else {
- return 0;
- }
-}
int is_reg(unsigned tok) {
return ((tok >= FIRST_REGISTER) && (tok <= LAST_REGISTER));
@@ -458,42 +407,32 @@ void expect_register(unsigned got) {
#define REG(n) (tnames[FIRST_REGISTER + (n)])
// various fields
-#define _OP(n) (((n) & 15) << 12)
-#define _A(n) (((n) & 15) << 8)
-#define _C(n) (((n) & 15) << 8)
-#define _B(n) (((n) & 15) << 4)
-#define _F(n) (((n) & 15) << 0)
-#define _I4ALU(n) (((n) & 15) << 4)
-#define _I4MEM(n) (((n) & 15) << 0)
-#define _I8(n) ((n) & 0xFF)
-#define _I12(n) ((n) & 0x7FF)
-#define _N(n) (((n) & 0xFF) << 4)
-
-#define OP_ALU_RA_RA_RB 0x0000
-#define OP_MOV_RA_S8 0x1000
-#define OP_ALU_RA_RA_S4 0x2000
-#define OP_ALU_RB_RA_U16 0x3000
-#define OP_ALU_R0_RA_RB 0x4000
-#define OP_ALU_R1_RA_RB 0x5000
-#define OP_ALU_R2_RA_RB 0x6000
-#define OP_ALU_R3_RA_RB 0x7000
-#define OP_LW_RB_S4 0x8000
-#define OP_SW_RB_S4 0x9000
-#define OP_B_C_S8 0xA000
-#define OP_B_C_RB 0xB000
-#define OP_BL_C_RB 0xB008
-#define OP_IRET_RB 0xBF00
-#define OP_MOV_RB_SA 0xB002
-#define OP_MOV_SA_RB 0xB003
-#define OP_SET 0xB004
-#define OP_CLR 0xB005
-#define OP_SYSCALL 0xB006
-#define OP_BREAK 0xB007
-#define OP_B_S12 0xC000
-#define OP_BL_S12 0xD000
-#define OP_MOV_XA_RB 0xE000
-#define OP_MOV_RA_XB 0xE001
-#define OP_UNKNONW 0xF000
+#define _OP(n) (((n) & 15) << 0)
+#define _A(n) (((n) & 15) << 4)
+#define _B(n) (((n) & 15) << 8)
+#define _FHI(n) (((n) & 15) << 12)
+#define _FLO(n) (((n) & 15) << 8)
+#define _I4(n) (((n) & 15) << 12)
+#define _I8(n) (((n) & 0xFF) << 8)
+#define _I12(n) (((n) & 0xFFF) << 4)
+
+#define OP_MOV_RA_S8 0x0000
+#define OP_MHI_RA_S8 0x0001
+#define OP_ALU_RA_RA_RB 0x0002
+#define OP_ALU_RA_RA_S4 0x0003
+#define OP_ALU_R0_RA_RB 0x0004
+#define OP_ALU_R1_RA_RB 0x0005
+#define OP_ALU_R2_RA_RB 0x0006
+#define OP_ALU_R3_RA_RB 0x0007
+#define OP_LW_RB_S4 0x0008
+#define OP_SW_RB_S4 0x0009
+#define OP_BNZ_RA_S8 0x000A
+#define OP_BZ_RA_S8 0x000B
+#define OP_B_S12 0x000C
+#define OP_BL_S12 0x000D
+#define OP_B_RB 0x000E
+#define OP_BL_RB 0x100E
+#define OP_NOP 0x200E
#define ALU_MOV 0
#define ALU_AND 1
@@ -533,13 +472,12 @@ void assemble_line(int n, unsigned *tok, unsigned *num, char **str) {
/* blank lines are fine */
return;
case tNOP:
- /* MOV r0, r0, r0 */
- emit(0);
+ emit(OP_NOP);
return;
case tNOT:
/* XOR rX, rX, -1 */
expect_register(T1);
- emit(OP_ALU_RA_RA_S4 | _A(to_reg(T1)) | _I4ALU(-1) | ALU_XOR);
+ emit(OP_ALU_RA_RA_S4 | _A(to_reg(T1)) | _I4(-1) | ALU_XOR);
return;
case tMOV:
expect_register(T1);
@@ -553,9 +491,18 @@ void assemble_line(int n, unsigned *tok, unsigned *num, char **str) {
if (is_signed8(num[4])) {
emit(OP_MOV_RA_S8 | _A(to_reg(T1)) | _I8(num[4]));
return;
+ } else {
+ emit(OP_MOV_RA_S8 | _A(to_reg(T1)) | _I8(num[4]));
+ emit(OP_MHI_RA_S8 | _A(to_reg(T1)) | _I8((num[4] >> 8)));
}
- emit(OP_ALU_RB_RA_U16 | _B(to_reg(T1)) | ALU_MOV);
- emit(num[4]);
+ return;
+ case tMHI:
+ expect_register(T1);
+ expect(tCOMMA, T2);
+ expect(tHASH, T3);
+ expect(tNUMBER, T4);
+ // range
+ emit(OP_MHI_RA_S8 | _A(to_reg(T1)) | _I8(num[4]));
return;
case tLW:
case tSW:
@@ -572,14 +519,50 @@ void assemble_line(int n, unsigned *tok, unsigned *num, char **str) {
expect(tCBRACK, T5);
tmp = 0;
}
- if (!is_signed8(tmp)) die("index too large");
- emit(instr | _A(to_reg(T1)) | _B(to_reg(T4)) | _I4MEM(tmp));
+ if (!is_signed4(tmp)) die("index too large");
+ emit(instr | _A(to_reg(T1)) | _B(to_reg(T4)) | _I4(tmp));
+ return;
+ case tB:
+ case tBL:
+ if (is_reg(T1)) {
+ instr = (T0 == tB) ? OP_B_RB : OP_BL_RB;
+ emit(instr | _B(to_reg(T1)));
+ } else {
+ instr = (T0 == tB) ? OP_B_S12 : OP_BL_S12;
+ if (T1 == tSTRING) {
+ emit(instr);
+ uselabel(str[1], PC - 1, TYPE_PCREL_S12);
+ } else if (T1 == tDOT) {
+ emit(instr | _I12(-1));
+ } else {
+ die("expected register or address");
+ }
+ }
+ return;
+ case tBZ:
+ case tBNZ:
+ instr = (T0 == tBZ) ? OP_BZ_RA_S8 : OP_BNZ_RA_S8;
+ expect_register(T1);
+ expect(tCOMMA, T2);
+ if (T3 == tSTRING) {
+ emit(instr | _A(to_reg(T1)));
+ uselabel(str[3], PC - 1, TYPE_PCREL_S8);
+ } else if (T3 == tDOT) {
+ emit(instr | _A(to_reg(T1)) | _I12(-1));
+ } else {
+ die("expected register or address");
+ }
return;
case tWORD:
tmp = 1;
for (;;) {
- expect(tNUMBER, tok[tmp]);
- emit(num[tmp++]);
+ if (tok[tmp] == tSTRING) {
+ emit(0);
+ uselabel(str[tmp++], PC - 1, TYPE_ABS_U16);
+ } else {
+ expect(tNUMBER, tok[tmp]);
+ emit(num[tmp++]);
+ }
if (tok[tmp] != tCOMMA)
break;
tmp++;
@@ -603,41 +586,25 @@ void assemble_line(int n, unsigned *tok, unsigned *num, char **str) {
return;
}
}
- if (is_branch(T0)) {
- unsigned cc = to_cc(T0);
- int link = is_branch_link(T0);
- if (is_reg(T1)) {
- emit((link ? OP_BL_C_RB : OP_B_C_RB) | _B(to_reg(T1)) | _C(cc));
- } else if (T1 == tSTRING) {
- if (cc == COND_AL) {
- emit(link ? OP_BL_S12 : OP_B_S12);
- uselabel(str[1], PC - 1, TYPE_PCREL_S12);
- } else {
- if (link) die("conditional, relative, linked branches unsupported");
- emit(OP_B_C_S8 | _C(cc));
- uselabel(str[1], PC - 1, TYPE_PCREL_S8);
- }
- } else if (T1 == tDOT) {
- if (link) die("nope");
- emit(OP_B_C_S8 | _C(cc) | _I8(-1));
- }
- return;
- }
if (is_alu_op(T0)) {
expect_register(T1);
expect(T2, tCOMMA);
expect_register(T3);
expect(T4, tCOMMA);
if ((T5 == tHASH) && (T6 == tNUMBER)) {
- if (is_signed4(num[6]) && (T1 == T3)) {
- emit(OP_ALU_RA_RA_S4 | _A(to_reg(T1)) | _F(to_func(T0)) | _I4ALU(num[6]));
+ if (T1 != T3) {
+ die("both registers must be the same in this form");
+ }
+ if (is_signed4(num[6])) {
+ emit(OP_ALU_RA_RA_S4 | _A(to_reg(T1)) | _FLO(to_func(T0)) | _I4(num[6]));
return;
+ } else {
+ // auto use R15 as scratch?
+ die("immediate out of range (s4)");
}
- emit(OP_ALU_RB_RA_U16 | _B(to_reg(T1)) | _A(to_reg(T3)) | _F(to_func(T0)));
- emit(num[6]);
} else if (is_reg(T5)) {
if (T1 == T3) {
- emit(OP_ALU_RA_RA_RB | _A(to_reg(T1)) | _B(to_reg(T5)) | _F(to_func(T0)));
+ emit(OP_ALU_RA_RA_RB | _A(to_reg(T1)) | _B(to_reg(T5)) | _FHI(to_func(T0)));
return;
}
switch (T1) {
@@ -648,7 +615,7 @@ void assemble_line(int n, unsigned *tok, unsigned *num, char **str) {
default:
die("three-reg ALU ops require R0-R3 for the first register");
}
- emit(instr | _A(to_reg(T3)) | _B(to_reg(T5)) | _F(to_func(T0)));
+ emit(instr | _A(to_reg(T3)) | _B(to_reg(T5)) | _FHI(to_func(T0)));
} else {
die("expected register or #, got %s", tnames[tok[5]]);
}
diff --git a/src/d16.c b/src/d16.c
@@ -25,29 +25,26 @@ char *append_int(char *buf, int n) {
return buf + strlen(buf);
}
-const char *condcode[] = {
- "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
- "HI", "LS", "GE", "LT", "GT", "LE", "", "NV",
-};
-
const char *regname[] = {
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
- "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"
+ "R8", "R9", "R10", "R11", "R12", "SP", "LR", "R15",
};
const char *alufunc[] = {
"MOV", "AND", "ORR", "XOR", "ADD", "SUB", "SHR", "SHL",
};
-int printinst(char *buf, unsigned pc, unsigned instr, unsigned next, const char *fmt) {
- int words = 1;
- unsigned a = (instr >> 8) & 15;
- unsigned b = (instr >> 4) & 15;
- unsigned f = (instr >> 0) & 15;
- int s4alu = (b & 0x8) ? (b | 0xFFFFFFF0) : (b & 0xF);
- int s4mem = (instr & 0x8) ? (instr | 0xFFFFFFF0) : (instr & 0xF);
- int s8 = (instr & 0x80) ? (instr | 0xFFFFFF00) : (instr & 0xFF);
- int s12 = (instr & 0x800) ? (instr | 0xFFFF800) : (instr & 0xFFF);
+void printinst(char *buf, unsigned pc, unsigned instr, const char *fmt) {
+ unsigned a = (instr >> 4) & 15;
+ unsigned b = (instr >> 8) & 15;
+ unsigned fhi = (instr >> 12) & 15;
+ unsigned flo = (instr >> 8) & 15;
+ unsigned i8 = (instr >> 8);
+ unsigned i4 = (instr >> 12);
+ unsigned i12 = (instr >> 4);
+ int s4 = (i4 & 0x8) ? (i4 | 0xFFFFFFF0) : (i4 & 0xF);
+ int s8 = (i8 & 0x80) ? (i8 | 0xFFFFFF00) : (i8 & 0xFF);
+ int s12 = (i12 & 0x800) ? (i12 | 0xFFFFF800) : (i12 & 0xFFF);
while (*fmt) {
if (*fmt != '@') {
@@ -62,19 +59,16 @@ int printinst(char *buf, unsigned pc, unsigned instr, unsigned next, const char
buf = append(buf, regname[b]);
break;
case 'C':
- buf = append(buf, condcode[a]);
+ buf = append(buf, regname[instr & 3]);
break;
case 'F':
- buf = append(buf, alufunc[f]);
+ buf = append(buf, alufunc[fhi]);
break;
case 'f': // alt alu func
- buf = append(buf, alufunc[b]);
- break;
- case 'i':
- buf = append_int(buf, s4alu);
+ buf = append(buf, alufunc[flo]);
break;
case '4':
- buf = append_int(buf, s4mem);
+ buf = append_int(buf, s4);
break;
case '8':
buf = append_int(buf, s8);
@@ -82,11 +76,6 @@ int printinst(char *buf, unsigned pc, unsigned instr, unsigned next, const char
case 's':
buf = append_int(buf, s12);
break;
- case 'U':
- words = 2;
- buf = append(buf, "0x");
- buf = append_u16(buf, next);
- break;
case 0:
goto done;
}
@@ -94,7 +83,6 @@ int printinst(char *buf, unsigned pc, unsigned instr, unsigned next, const char
}
done:
*buf = 0;
- return words;
}
struct {
@@ -102,41 +90,48 @@ struct {
u16 value;
const char *fmt;
} decode[] = {
- { 0b1111111111111111, 0b0000000000000000, "NOP" },
- { 0b1111000000001111, 0b0000000000000000, "MOV @A, @B" },
- { 0b1111000000000000, 0b0000000000000000, "@F @A, @A, @B" },
- { 0b1111000000000000, 0b0001000000000000, "MOV @A, #@8" },
- { 0b1111000000001111, 0b0010000000000000, "MOV @A, #@i" },
- { 0b1111000000000000, 0b0010000000000000, "@F @A, @A, #@i" },
- { 0b1111000000001111, 0b0011000000000000, "MOV @B, #@U" },
- { 0b1111000000000000, 0b0011000000000000, "@F @B, @A, #@U" },
- { 0b1111000000001111, 0b0100000000000000, "MOV R0, @B" },
- { 0b1111000000000000, 0b0100000000000000, "@F R0, @A, @B" },
- { 0b1111000000001111, 0b0101000000000000, "MOV R1, @B" },
- { 0b1111000000000000, 0b0101000000000000, "@F R1, @A, @B" },
- { 0b1111000000001111, 0b0110000000000000, "MOV R2, @B" },
- { 0b1111000000000000, 0b0110000000000000, "@F R2, @A, @B" },
- { 0b1111000000001111, 0b0111000000000000, "MOV R3, @B" },
- { 0b1111000000000000, 0b0111000000000000, "@F R3, @A, @B" },
- { 0b1111000000001111, 0b1000000000000000, "LW @A, [@B]" },
- { 0b1111000000000000, 0b1000000000000000, "LW @A, [@B, @4]" },
- { 0b1111000000001111, 0b1001000000000000, "SW @A, [@B]" },
- { 0b1111000000000000, 0b1001000000000000, "SW @A, [@B, @4]" },
- { 0b1111000000000000, 0b1010000000000000, "B@C @8" },
- { 0b1111000000001000, 0b1011000000000000, "B@C @B" },
- { 0b1111000000001000, 0b1011000000001000, "BL@C @B" },
- { 0b1111000000000000, 0b1100000000000000, "B @s" },
- { 0b1111000000000000, 0b1101000000000000, "BL @s" },
+ { 0b0000000000001111, 0b0000000000000000, "MOV @A, @8" },
+ { 0b0000000000001111, 0b0000000000000001, "MHI @A, @8" },
+ { 0b1111000000001111, 0b0000000000000010, "MOV @A, @B" },
+ { 0b0000000000001111, 0b0000000000000010, "@F @A, @B" },
+ { 0b0000111100001111, 0b0000000000000011, "MOV @A, @4" },
+ { 0b0000000000001111, 0b0000000000000011, "@f @A, @4" },
+ { 0b1111000000001100, 0b0000000000000100, "MOV @C, @B" },
+ { 0b0000000000001100, 0b0000000000000100, "@F @C, @B, @A" },
+ { 0b0000000000001111, 0b0000000000001000, "LW @A, [@B, @4]" },
+ { 0b0000000000001111, 0b0000000000001001, "SW @A, [@B, @4]" },
+ { 0b0000000000001111, 0b0000000000001010, "BNZ @A, @8" },
+ { 0b0000000000001111, 0b0000000000001011, "BZ @A, @8" },
+ { 0b0000000000001111, 0b0000000000001100, "B @s" },
+ { 0b0000000000001111, 0b0000000000001101, "BL @s" },
+ { 0b1111000000001111, 0b0000000000001110, "B @B" },
+ { 0b1111000000001111, 0b0001000000001110, "BL @B" },
+ { 0b1111000000001111, 0b0010000000001110, "NOP" },
{ 0b0000000000000000, 0b0000000000000000, "UNDEFINED" },
};
-int disassemble(char *buf, unsigned pc, unsigned instr, unsigned next) {
+void disassemble(char *buf, unsigned pc, unsigned instr) {
int n = 0;
for (n = 0 ;; n++) {
if ((instr & decode[n].mask) == decode[n].value) {
- return printinst(buf, pc, instr, next, decode[n].fmt);
+ printinst(buf, pc, instr, decode[n].fmt);
+ return;
}
}
- return 1;
+ buf[0] = 0;
}
+#ifdef STANDALONE
+int main(int argc, char **argv) {
+ char buf[256];
+ char line[1024];
+ while (fgets(line, 1024, stdin)) {
+ unsigned insn = 0xFFFF;
+ sscanf(line, "%x", &insn);
+ disassemble(buf, 0, insn);
+ printf("%s\n", buf);
+ fflush(stdout);
+ }
+ return 0;
+}
+#endif
diff --git a/src/test.s b/src/test.s
@@ -1,9 +1,10 @@
mov r0, #0
mov r1, #1
+ mov r1, #7
mov r2, #2
mov r3, #3
+ add r3, r3, #1
mov r3, #0xFEED
-
add r2, r2, #1
add r2, r2, #1
add r2, r2, #1
@@ -21,6 +22,7 @@
mov r0, #0xE000
lw r3, [r0]
+ mov r2, r3
mov r0, #0xDEAD
b .
@@ -28,5 +30,5 @@ fill: // r0=addr r1=value r2=count
sw r1, [r0]
add r0, r0, #1
sub r2, r2, #1
- bnz fill
+ bnz r2, fill
b lr
diff --git a/wave/cpu_sel_alu_op.txt b/wave/cpu_sel_alu_op.txt
@@ -0,0 +1,4 @@
+0 MOV
+1 MHI
+2 FLO
+3 FHI
diff --git a/wave/cpu_sel_bdata.txt b/wave/cpu_sel_bdata.txt
@@ -1,5 +1,4 @@
0 RB
-1 IR
-2 PC
-3 S4
-7 S8
+1 PC
+2 S4
+3 S8
diff --git a/wave/cpu_sel_branch.txt b/wave/cpu_sel_branch.txt
@@ -1,4 +1,3 @@
-0 INC
-1 S8
-2 S12
-3 REG
+0 S8
+1 S12
+2 RB