/****************************************************************************** * * Instruction decoder module * *****************************************************************************/ `include "def-fields.v" `include "def-alu.v" module saturn_decoder( i_clk, i_reset, i_cycles, i_en_dbg, i_en_dec, i_stalled, i_pc, i_nibble, i_reg_p, o_inc_pc, o_push, o_pop, o_dec_error, o_alu_debug, o_ins_addr, o_ins_decoded, o_fields_table, o_field, o_field_valid, o_field_start, o_field_last, o_imm_value, o_alu_op, o_alu_no_stall, o_reg_dest, o_reg_src1, o_reg_src2, o_direction, o_ins_rtn, o_set_xm, o_set_carry, o_en_intr, o_carry_val, o_ins_set_mode, o_mode_dec, o_ins_alu_op, o_dbg_nibbles, o_dbg_nb_nbls, o_mem_load, o_mem_pos ); /* * module input / output ports */ input wire [0:0] i_clk; input wire [0:0] i_reset; input wire [31:0] i_cycles; input wire i_en_dbg; input wire i_en_dec; input wire i_stalled; input wire [19:0] i_pc; input wire [3:0] i_nibble; input wire [3:0] i_reg_p; output reg o_inc_pc; output reg o_push; output reg o_pop; output reg o_dec_error; output reg o_alu_debug; // instructions related outputs output reg [19:0] o_ins_addr; output reg o_ins_decoded; output reg [1:0] o_fields_table; output reg [3:0] o_field; output reg o_field_valid; output reg [3:0] o_field_start; output reg [3:0] o_field_last; output reg [3:0] o_imm_value; output reg [4:0] o_alu_op; output reg [0:0] o_alu_no_stall; output reg [4:0] o_reg_dest; output reg [4:0] o_reg_src1; output reg [4:0] o_reg_src2; // generic output reg o_direction; // rtn specific output reg o_ins_rtn; output reg o_set_xm; output reg o_set_carry; output reg o_carry_val; output reg o_en_intr; // setdec/hex output reg o_ins_set_mode; output reg o_mode_dec; // alu_operations output reg o_ins_alu_op; /* data used by the debugger * */ output reg [(21*4-1):0] o_dbg_nibbles; output reg [4:0] o_dbg_nb_nbls; output reg [63:0] o_mem_load; output reg [4:0] o_mem_pos; /* * state registers */ reg [31:0] instr_ctr; reg [0:0] next_nibble; reg [4:0] inst_cycles; reg inval_opcode_regs; initial begin `ifdef SIM // $monitor({"i_clk %b | i_reset %b | i_cycles %d | i_en_dec %b | i_en_exec %b |", // " next_nibble %b | instr_start %b | i_nibble %h"}, // i_clk, i_reset, i_cycles, i_en_dec, i_en_exec, next_nibble, // instr_start, i_nibble); // $monitor("i_en_dec %b | i_cycles %d | nb %h | cont %b | b0x %b | rtn %b | sxm %b | sc %b | cv %b", // i_en_dec, i_cycles, i_nibble, next_nibble, block_0x, ins_rtn, set_xm, set_carry, carry_val); `endif end /* * debugger * */ wire [19:0] new_pc; assign new_pc = i_pc + 1; wire run_debugger; assign run_debugger = !i_reset && i_en_dbg && !i_stalled && !next_nibble; wire is_short_transfer; assign is_short_transfer = (o_field_last == 3) && ((o_reg_dest[4:1] == 4'b0010) || (o_reg_src1[4:1] == 4'b0010)); wire p_is_dest; wire is_load_imm; wire is_d0_eq; wire is_d1_eq; wire is_p_eq; wire is_la_hex; wire is_lc_hex; wire disp_nb_nibbles; assign p_is_dest = (o_reg_dest == `ALU_REG_P); assign is_load_imm = ((o_alu_op == `ALU_OP_COPY) || (o_alu_op == `ALU_OP_RST_BIT) || (o_alu_op == `ALU_OP_SET_BIT) || (o_alu_op == `ALU_OP_JMP_REL3) || (o_alu_op == `ALU_OP_JMP_REL4) || (o_alu_op == `ALU_OP_JMP_ABS5)) && (o_reg_src1 == `ALU_REG_IMM); assign is_d0_eq = is_load_imm && (o_reg_dest == `ALU_REG_D0); assign is_d1_eq = is_load_imm && (o_reg_dest == `ALU_REG_D1); assign is_p_eq = is_load_imm && p_is_dest; assign is_la_hex = is_load_imm && (o_reg_dest == `ALU_REG_A); assign is_lc_hex = is_load_imm && (o_reg_dest == `ALU_REG_C); assign disp_nb_nibbles = is_d0_eq || is_d1_eq; reg [4:0] nibble_pos; always @(posedge i_clk) begin if (run_debugger) begin /* * this whole thing is a large print statement * THIS PART IS NEVER GENERATED */ `ifdef SIM if (o_ins_decoded) begin $write("DBG 0: "); $write("%5h ", o_ins_addr); // $write("[%2d] ", o_dbg_nb_nbls); for(nibble_pos=0; nibble_pos!=o_dbg_nb_nbls; nibble_pos=nibble_pos+1) $write("%h", o_dbg_nibbles[nibble_pos*4+:4]); for(nibble_pos=o_dbg_nb_nbls; nibble_pos!=22; nibble_pos=nibble_pos+1) $write(" "); // display decoded instruction if (o_ins_rtn) begin $write("RT%s", o_en_intr?"I":"N"); if (o_set_xm) $write("SXM"); if (o_set_carry) $write("%sC", o_carry_val?"S":"C"); $display(""); end if (o_ins_set_mode) begin $display("SET%s", o_mode_dec?"DEC":"HEX"); end if (o_ins_alu_op) begin case (o_alu_op) `ALU_OP_JMP_REL3: $write("GOTO"); `ALU_OP_JMP_REL4: $write("%s", o_push?"GOSUBL":"GOLONG"); `ALU_OP_JMP_ABS5: $write("%s", o_push?"GOSBVL":"GOVLNG"); default: case (o_reg_dest) `ALU_REG_A: $write("A"); `ALU_REG_B: $write("B"); `ALU_REG_C: if (is_lc_hex) $write("LCHEX"); else $write("C"); `ALU_REG_D: $write("D"); `ALU_REG_D0: $write("D0"); `ALU_REG_D1: $write("D1"); `ALU_REG_RSTK: $write("RSTK"); `ALU_REG_R0: $write("R0"); `ALU_REG_R1: $write("R1"); `ALU_REG_R2: $write("R2"); `ALU_REG_R3: $write("R3"); `ALU_REG_R4: $write("R4"); `ALU_REG_DAT0: $write("DAT0"); `ALU_REG_DAT1: $write("DAT1"); `ALU_REG_ST: if (o_alu_op!=`ALU_OP_ZERO) $write("ST"); `ALU_REG_P: $write("P"); default: $write("[dest:%d]", o_reg_dest); endcase endcase case (o_alu_op) `ALU_OP_ZERO: if (o_reg_dest==`ALU_REG_ST) $write("CLRST"); else $write("=0"); `ALU_OP_COPY, `ALU_OP_AND, `ALU_OP_OR, `ALU_OP_INC, `ALU_OP_DEC, `ALU_OP_ADD, `ALU_OP_SUB, `ALU_OP_RST_BIT, `ALU_OP_SET_BIT: if (!is_lc_hex) $write("="); `ALU_OP_EXCH, `ALU_OP_JMP_REL3, `ALU_OP_JMP_REL4, `ALU_OP_JMP_ABS5: begin end default: $write("[op:%d]", o_alu_op); endcase case (o_alu_op) `ALU_OP_COPY, `ALU_OP_EXCH, `ALU_OP_AND, `ALU_OP_OR, `ALU_OP_INC, `ALU_OP_DEC, `ALU_OP_ADD, `ALU_OP_SUB: case (o_reg_src1) `ALU_REG_A: $write("A"); `ALU_REG_B: $write("B"); `ALU_REG_C: $write("C"); `ALU_REG_D: $write("D"); `ALU_REG_D0: $write("D0"); `ALU_REG_D1: $write("D1"); `ALU_REG_RSTK: $write("RSTK"); `ALU_REG_R0: $write("R0"); `ALU_REG_R1: $write("R1"); `ALU_REG_R2: $write("R2"); `ALU_REG_R3: $write("R3"); `ALU_REG_R4: $write("R4"); `ALU_REG_DAT0: $write("DAT0"); `ALU_REG_DAT1: $write("DAT1"); `ALU_REG_ST: $write("ST"); `ALU_REG_P: $write("P"); `ALU_REG_IMM: if (disp_nb_nibbles) if (o_mem_pos < 9) $write("(%1d)", o_mem_pos+1); else $write("(%2d)", o_mem_pos+1); default: $write("[src1:%d]", o_reg_src1); endcase `ALU_OP_RST_BIT: $write("0"); `ALU_OP_SET_BIT: $write("1"); endcase if ((o_alu_op == `ALU_OP_COPY) && is_short_transfer) $write("S"); if (o_alu_op == `ALU_OP_EXCH) $write("%s", is_short_transfer?"XS":"EX"); case (o_alu_op) `ALU_OP_AND, `ALU_OP_OR, `ALU_OP_ADD, `ALU_OP_SUB: begin case (o_alu_op) `ALU_OP_AND: $write("&"); `ALU_OP_OR: $write("!"); `ALU_OP_ADD: $write("+"); `ALU_OP_SUB: $write("-"); default: $write("[op:%d]", o_alu_op); endcase case (o_reg_src2) `ALU_REG_A: $write("A"); `ALU_REG_B: $write("B"); `ALU_REG_C: $write("C"); `ALU_REG_D: $write("D"); `ALU_REG_RSTK: $write("RSTK"); `ALU_REG_IMM: if (o_imm_value < 9) $write("\t%1d", o_imm_value+1); else $write("\t%2d", o_imm_value+1); default: $write("[src2:%d]", o_reg_src2); endcase end `ALU_OP_INC: $write("+1"); `ALU_OP_DEC: $write("-1"); `ALU_OP_ZERO, `ALU_OP_COPY, `ALU_OP_EXCH: begin end endcase // if (!((o_reg_dest == `ALU_REG_RSTK) || (o_reg_src1 == `ALU_REG_RSTK) || // (o_reg_dest == `ALU_REG_ST) || (o_reg_src1 == `ALU_REG_ST ) || // (o_reg_dest == `ALU_REG_P) || (o_reg_src1 == `ALU_REG_P ))) begin $write("\t"); if (o_field_valid) begin // $write("[FT%d]", o_fields_table); if (o_fields_table != `FT_TABLE_value) case (o_field) `FT_FIELD_P: $write("P"); `FT_FIELD_WP: $write("WP"); `FT_FIELD_XS: $write("XS"); `FT_FIELD_X: $write("X"); `FT_FIELD_S: $write("S"); `FT_FIELD_M: $write("M"); `FT_FIELD_B: $write("B"); `FT_FIELD_W: $write("W"); `FT_FIELD_A: $write("A"); endcase else if (o_field_last < 9) $write("%1d", o_field_last+1); else $write("%2d", o_field_last+1); end // $write("@%b@", is_load_imm); if (is_load_imm) begin if (is_p_eq) begin if (o_imm_value < 10) $write("%1d", o_imm_value); else $write("%2d", o_imm_value); end else begin for(nibble_pos=(o_mem_pos - 1); nibble_pos!=31; nibble_pos=nibble_pos-1) $write("%h", o_mem_load[nibble_pos*4+:4]); end end else case (o_reg_dest) `ALU_REG_P, `ALU_REG_ST: begin end default: $write("[%h:%h]", o_field_start, o_field_last); endcase $display("\t(%d cycles)", inst_cycles); end end // $display("new [%5h]--------------------------------------------------------------------", new_pc); `endif end end /****************************************************************************** * * handles part of the instruction decoding, * acts as the main FSM * *****************************************************************************/ // general variables reg use_fields_tbl; reg block_0x; reg block_0Efx; reg block_1x; reg block_save_to_R_W; reg block_rest_from_R_W; reg block_exch_with_R_W; reg block_pointer_assign_exch; reg block_mem_transfer; reg block_pointer_arith_const; reg block_load_p; reg block_load_c_hex; reg block_jmp2_cry_set; reg block_jmp2_cry_clr; reg block_8x; reg block_80x; reg go_fields_table; /* lots'o-wires to decode common states */ wire count_cycles; wire decoder_active; wire decoder_stalled; wire do_on_first_nibble; wire do_on_other_nibbles; assign count_cycles = !i_reset && i_en_dec && (next_nibble || i_stalled); assign decoder_active = !i_reset && i_en_dec && !i_stalled; assign decoder_stalled = !i_reset && i_en_dec && i_stalled; assign do_on_first_nibble = decoder_active && !next_nibble; assign do_on_other_nibbles = decoder_active && next_nibble; wire do_block_0x; wire do_block_0Efx; wire do_block_1x; wire do_block_save_to_R_W; wire do_block_rest_from_R_W; wire do_block_exch_with_R_W; wire do_block_Rn_A_C; wire do_block_pointer_assign_exch; wire do_block_mem_transfer; wire do_block_pointer_arith_const; wire do_block_load_p; wire do_block_load_c_hex; wire do_block_jmp2_cry_set; wire do_block_jmp2_cry_clr; wire do_block_8x; wire do_block_80x; assign do_block_0x = do_on_other_nibbles && block_0x; assign do_block_0Efx = do_on_other_nibbles && block_0Efx; assign do_block_1x = do_on_other_nibbles && block_1x; assign do_block_save_to_R_W = do_on_other_nibbles && block_save_to_R_W; assign do_block_rest_from_R_W = do_on_other_nibbles && block_rest_from_R_W; assign do_block_exch_with_R_W = do_on_other_nibbles && block_exch_with_R_W; assign do_block_Rn_A_C = do_on_other_nibbles && ( block_save_to_R_W || block_rest_from_R_W || block_exch_with_R_W ); assign do_block_pointer_assign_exch = do_on_other_nibbles && block_pointer_assign_exch; assign do_block_mem_transfer = do_on_other_nibbles && block_mem_transfer; assign do_block_pointer_arith_const = do_on_other_nibbles && block_pointer_arith_const; assign do_block_load_p = do_on_other_nibbles && block_load_p; assign do_block_load_c_hex = do_on_other_nibbles && block_load_c_hex; assign do_block_jmp2_cry_set = do_on_other_nibbles && block_jmp2_cry_set; assign do_block_jmp2_cry_clr = do_on_other_nibbles && block_jmp2_cry_clr; assign do_block_8x = do_on_other_nibbles && block_8x; assign do_block_80x = do_on_other_nibbles && block_80x; /* * subroutines */ reg block_load_reg_imm; reg block_jmp; reg block_sr_bit; wire do_load_reg_imm; wire do_block_jmp; wire do_block_sr_bit; assign do_load_reg_imm = do_on_other_nibbles && block_load_reg_imm; assign do_block_jmp = do_on_other_nibbles && block_jmp; assign do_block_sr_bit = do_on_other_nibbles && block_sr_bit; wire in_fields_table; assign in_fields_table = go_fields_table && !fields_table_done; /* * variables specific to a particular use */ reg [4:0] mem_load_max; /* most instructions are groupped by sets of 4 with * varrying series of registers that are common * this generates all the required series from i_nibble */ wire [4:0] dbg_write_pos; assign dbg_write_pos = (!next_nibble?0:o_dbg_nb_nbls); always @(posedge i_clk) begin if (i_reset) begin inst_cycles <= 0; next_nibble <= 0; use_fields_tbl <= 0; o_inc_pc <= 1; o_dec_error <= 0; o_alu_debug <= 0; o_ins_decoded <= 0; o_alu_op <= 0; end if (decoder_active) begin /* * stuff that is always done */ `ifdef SIM // $display("DEC_RUN 2: nibble %h", i_nibble); `endif o_inc_pc <= 1; // may be set to 0 later o_dbg_nibbles[dbg_write_pos*4+:4] <= i_nibble; o_dbg_nb_nbls <= o_dbg_nb_nbls + 1; end // if (decoder_stalled) begin // $display("DEC_STAL 2:"); // end if (count_cycles) begin inst_cycles <= inst_cycles + 1; end /* * cleanup */ if (do_on_first_nibble) begin inst_cycles <= 1; next_nibble <= 1; use_fields_tbl <= 0; o_alu_debug <= 0; o_push <= 0; o_pop <= 0; o_ins_decoded <= 0; // store the address where the instruction starts o_ins_addr <= i_pc; // decoder block states block_0x <= 0; block_0Efx <= 0; block_1x <= 0; block_save_to_R_W <= 0; block_rest_from_R_W <= 0; block_exch_with_R_W <= 0; block_pointer_assign_exch <= 0; block_mem_transfer <= 0; block_pointer_arith_const <= 0; block_load_p <= 0; block_load_c_hex <= 0; block_jmp2_cry_set <= 0; block_jmp2_cry_clr <= 0; block_8x <= 0; block_80x <= 0; // decoder subroutine states block_load_reg_imm <= 0; block_jmp <= 0; block_sr_bit <= 0; // cleanup fields table variables go_fields_table <= 0; o_fields_table <= 3; o_field <= 0; o_field_start <= 0; o_field_last <= 0; o_alu_op <= 0; o_alu_no_stall <= 0; // cleanup o_direction <= 0; o_ins_rtn <= 0; o_set_xm <= 0; o_set_carry <= 0; o_carry_val <= 0; o_ins_set_mode <= 0; o_mode_dec <= 0; o_ins_alu_op <= 0; o_dbg_nb_nbls <= 1; o_mem_pos <= 0; /* * x first nibble */ // assign block regs case (i_nibble) 4'h0: block_0x <= 1; 4'h1: block_1x <= 1; 4'h2: block_load_p <= 1; 4'h3: block_load_c_hex <= 1; 4'h4: block_jmp2_cry_set <= 1; 4'h5: block_jmp2_cry_clr <= 1; 4'h6: begin o_alu_no_stall <= 1; o_alu_op <= `ALU_OP_JMP_REL3; mem_load_max <= 2; o_mem_pos <= 0; block_jmp <= 1; end 4'h8: block_8x <= 1; default: begin `ifdef SIM $display("DEC_INIT 2: nibble %h not handled", i_nibble); `endif o_dec_error <= 1; end endcase end /****************************************************************************** * * 0x * * 00 RTNSXM 08 CLRST * 01 RTN 09 C=ST * 02 RTNSC 0A ST=C * 03 RTNCC 0B CSTEX * 04 SETHEX 0C P=P+1 * 05 SETDEC 0D P=P-1 * 06 RSTK=C * 07 C=RSTK 0F RTI * *****************************************************************************/ if (do_block_0x) begin case (i_nibble) 4'h0, 4'h1, 4'h2, 4'h3, 4'hF: begin o_ins_rtn <= 1; o_set_xm <= i_nibble == 4'h0; o_set_carry <= !i_nibble[3] && i_nibble[1]; o_carry_val <= i_nibble[1] && i_nibble[0]; o_en_intr <= i_nibble[3]; end 4'h4, 4'h5: begin o_ins_set_mode <= 1; o_mode_dec <= (i_nibble[0]); end /* RSTK=C * C=RSTK * those 2 are alu copy ops between RSTK and C */ 4'h6, 4'h7: begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_COPY; o_push <= !i_nibble[0]; o_pop <= i_nibble[0]; end 4'h8: begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_ZERO; end 4'h9, 4'hA: begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_COPY; end 4'hB: begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_EXCH; end 4'hC, 4'hD: begin o_ins_alu_op <= 1; o_alu_op <= i_nibble[0]?`ALU_OP_DEC:`ALU_OP_INC; end 4'hE: begin block_0x <= 0; o_fields_table <= `FT_TABLE_f; end default: begin `ifdef SIM $display("block_0x: nibble %h not handled", i_nibble); `endif o_dec_error <= 1; end endcase next_nibble <= (i_nibble == 4'hE); block_0Efx <= (i_nibble == 4'hE); go_fields_table <= (i_nibble == 4'hE); o_ins_decoded <= (i_nibble != 4'hE); end /****************************************************************************** * * 0Ex R1=R1[&!]R2 table_f * *****************************************************************************/ if (do_block_0Efx && !in_fields_table) begin o_ins_alu_op <= 1; o_alu_op <= (!i_nibble[3])?`ALU_OP_AND:`ALU_OP_OR; next_nibble <= 0; o_ins_decoded <= 1; end /****************************************************************************** * * 1x jump table to other things * *****************************************************************************/ if (do_block_1x) begin case (i_nibble) 4'h0: // save A/C to Rn W block_save_to_R_W <= 1; 4'h1: // restore A/C from Rn W block_rest_from_R_W <= 1; 4'h2: // exchange A/C with Rn W block_exch_with_R_W <= 1; 4'h3: // move/exch A/C with Dn A/[0:3] block_pointer_assign_exch <= 1; 4'h4, 4'h5: // DAT[01]=[AC] begin block_mem_transfer <= 1; o_fields_table <= i_nibble[0]?`FT_TABLE_value:`FT_TABLE_f; use_fields_tbl <= i_nibble[0]; end 4'h6, 4'h7, 4'h8, 4'hC: // D[01]=D[01][+-] n+1; begin block_pointer_arith_const <= 1; o_ins_alu_op <= 1; o_alu_op <= i_nibble[1]?`ALU_OP_ADD:`ALU_OP_SUB; end 4'h9, 4'hA, 4'hB, 4'hD, 4'hE, 4'hF: // D[0]=([245]) begin mem_load_max <= {1'b0, i_nibble[1], !i_nibble[1], i_nibble[1] && i_nibble[0]}; o_mem_pos <= 0; block_load_reg_imm <= 1; o_alu_no_stall <= 1; o_alu_op <= `ALU_OP_COPY; end endcase block_1x <= 0; end if (do_block_save_to_R_W || do_block_rest_from_R_W) begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_COPY; next_nibble <= 0; o_ins_decoded <= 1; block_save_to_R_W <= 0; block_rest_from_R_W <= 0; end if (do_block_exch_with_R_W) begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_EXCH; next_nibble <= 0; o_ins_decoded <= 1; end if (do_block_pointer_assign_exch) begin o_ins_alu_op <= 1; o_alu_op <= i_nibble[1]?`ALU_OP_EXCH:`ALU_OP_COPY; next_nibble <= 0; o_ins_decoded <= 1; end if (do_block_mem_transfer) begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_COPY; // we next_nibble if we need the fields table (nibble2 was 5) go_fields_table <= use_fields_tbl; next_nibble <= use_fields_tbl; use_fields_tbl <= 0; o_ins_decoded <= !(use_fields_tbl); end if (do_block_pointer_arith_const) begin next_nibble <= 0; o_imm_value <= i_nibble; o_ins_decoded <= 1; end if (do_block_load_p) begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_COPY; o_imm_value <= i_nibble; next_nibble <= 0; o_ins_decoded <= 1; end if (do_block_load_c_hex) begin // $write("block load C hex %h\n", i_nibble); mem_load_max <= i_nibble + 1; o_mem_pos <= 0; o_alu_no_stall <= 1; o_alu_op <= `ALU_OP_COPY; block_load_reg_imm <= 1; block_load_c_hex <= 0; end if (do_block_jmp2_cry_clr) begin end if (do_block_8x) begin $display("block_8x %h | op %d", i_nibble, o_alu_op); case (i_nibble) 4'h0: // block_80x <= 1; 4'h4, 4'h5: // ST=[01] n begin o_alu_op <= i_nibble[0]?`ALU_OP_SET_BIT:`ALU_OP_RST_BIT; block_sr_bit <= 1; end 4'hC, 4'hD, 4'hE, 4'hF: // GOLONG, GOVLNG, GOSUBL, GOSBVL begin o_alu_no_stall <= 1; o_alu_op <= i_nibble[0]?`ALU_OP_JMP_ABS5:`ALU_OP_JMP_REL4; // is it a gosub ? o_push <= i_nibble[1]; o_alu_debug <= i_nibble[1]; mem_load_max <= i_nibble[0]?4:3; o_mem_pos <= 0; block_jmp <= 1; // debug for cases not tested o_alu_debug <= i_nibble[1] || !i_nibble[0]; end default: begin $display("block_8x %h error", i_nibble); o_dec_error <= 1; end endcase block_8x <= 0; end if (do_block_80x) begin $display("block_80x %h | op %d", i_nibble, o_alu_op); case (i_nibble) 4'hC: // C=P n begin o_ins_alu_op <= 1; o_alu_op <= `ALU_OP_COPY; o_alu_debug <= 1; next_nibble <= 0; o_ins_decoded <= 1; end default: begin $display("block_80x %h error", i_nibble); o_dec_error <= 1; end endcase block_80x <= 0; end // utilities if (do_load_reg_imm) begin // $write("load reg imm %h | ", i_nibble); // $write("pos %d | max %d | ", o_mem_pos, mem_load_max); // $write("next %b | dec %b | ", (o_mem_pos+1) != mem_load_max, (o_mem_pos+1) == mem_load_max); // $write("\n"); o_ins_alu_op <= 1; o_imm_value <= i_nibble; o_mem_load[o_mem_pos*4+:4] <= i_nibble; o_mem_pos <= o_mem_pos + 1; next_nibble <= (o_mem_pos+1) != mem_load_max; o_ins_decoded <= (o_mem_pos+1) == mem_load_max; end if (do_block_jmp) begin $display("do_block_jmp %h", i_nibble); o_ins_alu_op <= 1; o_imm_value <= i_nibble; o_mem_load[o_mem_pos*4+:4] <= i_nibble; o_mem_pos <= o_mem_pos + 1; next_nibble <= mem_load_max != o_mem_pos; o_ins_decoded <= mem_load_max == o_mem_pos; end if (do_block_sr_bit) begin $display("do_block_sr_bit %h", i_nibble); o_ins_alu_op <= 1; o_imm_value <= i_nibble; o_mem_load[3:0] <= i_nibble; o_mem_pos <= 1; next_nibble <= 0; o_ins_decoded <= 1; end end /****************************************************************************** * * set registers from instruction nibble * *****************************************************************************/ wire [4:0] reg_ABCD; wire [4:0] reg_BCAC; wire [4:0] reg_ABAC; wire [4:0] reg_BCCD; wire [4:0] reg_D0D1; wire [4:0] reg_DAT0DAT1; wire [4:0] reg_A_C; assign reg_ABCD = { 3'b000, i_nibble[1:0]}; assign reg_BCAC = { 3'b000, i_nibble[0], !(i_nibble[1] || i_nibble[0])}; assign reg_ABAC = { 3'b000, i_nibble[1] && i_nibble[0], (!i_nibble[1]) && i_nibble[0]}; assign reg_BCCD = { 3'b000, i_nibble[1] || i_nibble[0], (!i_nibble[1]) ^ i_nibble[0]}; // assign reg_D0D1 = { 4'b0010, (i_nibble[0] && i_nibble[1]) || (i_nibble[2] && i_nibble[3])}; assign reg_D0D1 = { 4'b0010, i_nibble[0]}; assign reg_DAT0DAT1 = { 4'b1000, i_nibble[0]}; assign reg_A_C = { 3'b000, i_nibble[2], 1'b0}; always @(posedge i_clk) begin if (i_reset) begin o_reg_dest <= 0; o_reg_src1 <= 0; o_reg_src2 <= 0; inval_opcode_regs <= 0; end if (do_on_first_nibble) begin // reset values on instruction decode start case (i_nibble) 4'h6: begin o_reg_dest <= 0; o_reg_src1 <= `ALU_REG_IMM; o_reg_src2 <= 0; end default: begin o_reg_dest <= 0; o_reg_src1 <= 0; o_reg_src2 <= 0; end endcase inval_opcode_regs <= 0; end /************************************************************************ * * set registers for specific instructions * ************************************************************************/ if (do_block_0x) begin case (i_nibble) 4'h6: begin o_reg_dest <= `ALU_REG_RSTK; o_reg_src1 <= `ALU_REG_C; end 4'h7: begin o_reg_dest <= `ALU_REG_C; o_reg_src1 <= `ALU_REG_RSTK; end 4'h8: o_reg_dest <= `ALU_REG_ST; 4'h9, 4'hB: begin o_reg_dest <= `ALU_REG_C; o_reg_src1 <= `ALU_REG_ST; end 4'hA: begin o_reg_dest <= `ALU_REG_ST; o_reg_src1 <= `ALU_REG_C; end 4'hC, 4'hD: begin o_reg_dest <= `ALU_REG_P; o_reg_src1 <= `ALU_REG_P; end default: begin // inval_opcode_regs <= 1; end endcase end if (do_block_0Efx && !in_fields_table) begin o_reg_dest <= i_nibble[2]?reg_BCAC:reg_ABCD; o_reg_src1 <= i_nibble[2]?reg_BCAC:reg_ABCD; o_reg_src2 <= i_nibble[2]?reg_ABCD:reg_BCAC; end if (do_block_1x) begin case (i_nibble) 4'h6, 4'h8: begin o_reg_dest <= `ALU_REG_D0; o_reg_src1 <= `ALU_REG_D0; end 4'h7, 4'hC: begin o_reg_dest <= `ALU_REG_D1; o_reg_src1 <= `ALU_REG_D1; end 4'h9, 4'hA, 4'hB: begin o_reg_dest <= `ALU_REG_D0; o_reg_src1 <= `ALU_REG_IMM; end 4'hD, 4'hE, 4'hF: begin o_reg_dest <= `ALU_REG_D1; o_reg_src1 <= `ALU_REG_IMM; end default: begin end endcase end if (do_block_save_to_R_W) begin o_reg_dest <= {2'b01, i_nibble[2:0]}; o_reg_src1 <= {3'b000, i_nibble[3]?2'b10:2'b00}; end if (do_block_rest_from_R_W || do_block_exch_with_R_W) begin o_reg_dest <= {3'b000, i_nibble[3]?2'b10:2'b00}; o_reg_src1 <= {2'b01, i_nibble[2:0]}; end if (do_block_pointer_assign_exch) begin o_reg_dest <= i_nibble[1]?reg_A_C:reg_D0D1; o_reg_src1 <= i_nibble[1]?reg_D0D1:reg_A_C; end if (do_block_mem_transfer) begin o_reg_dest <= i_nibble[1]?reg_A_C:reg_DAT0DAT1; o_reg_src1 <= i_nibble[1]?reg_DAT0DAT1:reg_A_C; end if (do_block_pointer_arith_const) begin o_reg_src2 <= `ALU_REG_IMM; end if (do_block_load_p) begin o_reg_dest <= `ALU_REG_P; o_reg_src1 <= `ALU_REG_IMM; end if (do_block_load_c_hex) begin o_reg_dest <= `ALU_REG_C; o_reg_src1 <= `ALU_REG_IMM; end if (do_block_8x) begin case (i_nibble) 4'h4, 4'h5, 4'h6, 4'h7: begin o_reg_dest <= `ALU_REG_ST; o_reg_src1 <= `ALU_REG_IMM; end 4'hC, 4'hD, 4'hE, 4'hF: begin o_reg_dest <= 0; o_reg_src1 <= `ALU_REG_IMM; o_reg_src2 <= 0; end endcase end if (do_block_80x) begin case (i_nibble) 4'hC: begin o_reg_dest <= `ALU_REG_C; o_reg_src1 <= `ALU_REG_P; o_reg_src2 <= 0; end endcase end end /****************************************************************************** * * set fields from instruction nibble * *****************************************************************************/ `ifdef SIM // `define DEBUG_FIELDS_TABLE `endif reg fields_table_done; /* more wires to decode common states. * can possibly be made less redundant / faster ? */ wire do_fields_table; assign do_fields_table = decoder_active && go_fields_table && !fields_table_done; wire table_a; wire table_b; wire table_f; wire table_value; assign table_a = (o_fields_table == `FT_TABLE_a); assign table_b = (o_fields_table == `FT_TABLE_b); assign table_f = (o_fields_table == `FT_TABLE_f); assign table_value = (o_fields_table == `FT_TABLE_value); wire do_tables_a_f_b; assign do_tables_a_f_b = do_fields_table && !table_value; wire table_f_bit_3; wire [3:0] table_a_f_b_case_value; assign table_f_bit_3 = table_f && i_nibble[3]; assign table_a_f_b_case_value = {table_f_bit_3, i_nibble[2:0]}; /* value generation for debug */ wire table_a_nb_ok; wire table_b_nb_ok; wire table_f_cond; wire table_f_nb_ok; wire table_a_f_b_nb_ok; assign table_a_nb_ok = table_a && !i_nibble[3]; assign table_b_nb_ok = table_b && i_nibble[3]; assign table_f_cond = !i_nibble[3] || (i_nibble == 4'hF); assign table_f_nb_ok = table_f && table_f_cond; assign table_a_f_b_nb_ok = table_a_nb_ok || table_b_nb_ok || table_f_nb_ok; /* here we go */ always @(posedge i_clk) begin if (i_reset || do_on_first_nibble) begin // reset values fields_table_done <= 0; o_field <= 0; o_field_valid <= 0; case (i_nibble) 4'h6: begin o_field_start <= 0; o_field_last <= 2; end default: begin o_field_start <= 0; o_field_last <= 0; end endcase end /****************************************************************************** * * set field for specific instructions * *****************************************************************************/ if (do_block_0x) begin case (i_nibble) 4'h6, 4'h7: begin // virtual A o_field_start <= 0; o_field_last <= 4; end 4'h8, 4'h9, 4'hA, 4'hB: begin // ST is 0-3 o_field_start <= 0; o_field_last <= 3; end default: begin end // don't care endcase end if (do_block_1x) begin o_field_start <= 0; case (i_nibble) 4'h9, 4'hD: begin o_field_last <= 1; end 4'hA, 4'hE: begin o_field_last <= 3; end 4'hB, 4'hF: begin o_field_last <= 4; end endcase end if (do_block_Rn_A_C) begin o_field_start <= 0; o_field_last <= 15; end if (do_block_pointer_assign_exch) begin o_field_start <= 0; o_field_last <= i_nibble[3]?3:4; end if (do_block_mem_transfer && !do_fields_table) begin o_field <= i_nibble[3]?`FT_FIELD_B:`FT_FIELD_A; o_field_start <= 0; o_field_last <= i_nibble[3]?1:4; o_field_valid <= 1; end if (do_block_mem_transfer && do_fields_table && table_value) begin o_field_start <= 0; o_field_last <= i_nibble; o_field_valid <= 1; end if (do_block_pointer_arith_const) begin o_field_start <= 0; o_field_last <= 4; end if (do_block_load_c_hex) begin o_field_start <= i_reg_p; o_field_last <= (i_nibble + i_reg_p) & 4'hF; end if (do_block_8x) begin case (i_nibble) 4'hC, 4'hD, 4'hE, 4'hF: begin o_field_start <= 0; o_field_last <= i_nibble[3]?4:3; end endcase end if (do_block_80x) begin case (i_nibble) 4'hC: begin o_field_start <= i_nibble; o_field_last <= i_nibble; end endcase end /****************************************************************************** * * set field from a table * * *****************************************************************************/ `ifdef DEBUG_FIELDS_TABLE if (do_tables_a_f_b) begin // debug info $display("====== fields_table | table %h | nibble %b", o_fields_table, i_nibble); $display("table_a : %b", table_a_nb_ok); $display("table_b : %b", table_b_nb_ok); $display("table_f_cond: %b", table_f_cond); $display("table_f : %b", table_f_nb_ok); // $display("table_f nbl : %h", {4{o_fields_table == `FT_TABLE_f}} ); //$display("table_f val : %h", table_f_nibble_value); $display("case nibble : %h", table_a_f_b_case_value); end `endif // if (do_tables_a_f_b) begin case (table_a_f_b_case_value) 4'h0: begin o_field <= `FT_FIELD_P; o_field_start <= i_reg_p; o_field_last <= i_reg_p; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field P (%h)", i_reg_p); `endif end 4'h1: begin o_field <= `FT_FIELD_WP; o_field_start <= 0; o_field_last <= i_reg_p; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field WP (0-%h)", i_reg_p); `endif end 4'h2: begin o_field <= `FT_FIELD_XS; o_field_start <= 2; o_field_last <= 2; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field XS"); `endif end 4'h3: begin o_field <= `FT_FIELD_X; o_field_start <= 0; o_field_last <= 2; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field X"); `endif end 4'h4: begin o_field <= `FT_FIELD_S; o_field_start <= 15; o_field_last <= 15; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field S"); `endif end 4'h5: begin o_field <= `FT_FIELD_M; o_field_start <= 3; o_field_last <= 14; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field M"); `endif end 4'h6: begin o_field <= `FT_FIELD_B; o_field_start <= 0; o_field_last <= 1; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field B"); `endif end 4'h7: begin o_field <= `FT_FIELD_W; o_field_start <= 0; o_field_last <= 15; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field W"); `endif end 4'hF: begin o_field <= `FT_FIELD_A; o_field_start <= 0; o_field_last <= 4; `ifdef DEBUG_FIELDS_TABLE $display("fields_table: field A"); `endif end default: begin o_dec_error <= 1; `ifdef SIM $display("fields_table: table %h nibble %h not handled", o_fields_table, i_nibble); `endif end endcase o_field_valid <= 1; fields_table_done <= 1; end end endmodule