gateware

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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:
MMakefile | 8++++----
Mhdl/cpu/alu.v | 94+++++++++++++++++++++++++++++--------------------------------------------------
Mhdl/cpu/cpu.v | 423+++++++++++++++++++++++++++++++++++++++----------------------------------------
Dhdl/cpu/cpu_pipelined.v | 378-------------------------------------------------------------------------------
Mhdl/cpu/isa.txt | 82++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mhdl/cpu/regfile.v | 18++++++++++++------
Msrc/a16.c | 233++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/d16.c | 107++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/test.s | 6++++--
Awave/cpu_sel_alu_op.txt | 4++++
Mwave/cpu_sel_bdata.txt | 7+++----
Mwave/cpu_sel_branch.txt | 7+++----
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