//////////////////////////////////////////////////////////////////////////////// // // Create Date: 10/05/07 // Module Name: pic_base // Description: Implement a PIC 16 based MPU // // // Revision: // Revision 0.01 - File Created // // File format: This file has been formated to use tabstop of 4 // // Development platform: // // Copyright (C) 2007, Rick Huang // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // //////////////////////////////////////////////////////////////////////////////// module pic_base (clk, reset, portb, porta); input clk; input reset; output [7:0] portb; // Output only port input [7:0] porta; // Input only port // Internal bus for now, expose later in the project reg [13:0] ibus_d; reg [10:0] reg_pc; // Program counter reg [7:0] fbus_do; reg [7:0] fbus_di; reg [7:0] fbus_a; reg fbus_wr; // Create a ROM for instruction storage // Put a small debug program here wire [13:0] ins_rom; assign ins_rom = (reg_pc == 11'h000) ? 14'h3000 : (reg_pc == 11'h001) ? 14'h00A0 : (reg_pc == 11'h002) ? 14'h3020 : (reg_pc == 11'h003) ? 14'h00A1 : (reg_pc == 11'h004) ? 14'h3030 : (reg_pc == 11'h005) ? 14'h00A2 : (reg_pc == 11'h006) ? 14'h3020 : (reg_pc == 11'h007) ? 14'h00A3 : (reg_pc == 11'h008) ? 14'h2018 : (reg_pc == 11'h009) ? 14'h00A2 : (reg_pc == 11'h00a) ? 14'h3055 : (reg_pc == 11'h00b) ? 14'h0621 : (reg_pc == 11'h00c) ? 14'h1D03 : (reg_pc == 11'h00d) ? 14'h2815 : (reg_pc == 11'h00e) ? 14'h30AA : (reg_pc == 11'h00f) ? 14'h0622 : (reg_pc == 11'h010) ? 14'h1D03 : (reg_pc == 11'h011) ? 14'h2815 : (reg_pc == 11'h012) ? 14'h30FF : (reg_pc == 11'h013) ? 14'h0086 : (reg_pc == 11'h014) ? 14'h2817 : (reg_pc == 11'h015) ? 14'h3000 : (reg_pc == 11'h016) ? 14'h0086 : (reg_pc == 11'h017) ? 14'h2817 : (reg_pc == 11'h018) ? 14'h3055 : (reg_pc == 11'h019) ? 14'h00A1 : (reg_pc == 11'h01a) ? 14'h34AA : (reg_pc == 11'h01b) ? 14'h0000 : (reg_pc == 11'h01c) ? 14'h0621 : (reg_pc == 11'h01d) ? 14'h1D03 : (reg_pc == 11'h01e) ? 14'h283E : (reg_pc == 11'h01f) ? 14'h3060 : (reg_pc == 11'h020) ? 14'h00A1 : (reg_pc == 11'h021) ? 14'h3020 : (reg_pc == 11'h022) ? 14'h02A1 : (reg_pc == 11'h023) ? 14'h3040 : (reg_pc == 11'h024) ? 14'h0621 : (reg_pc == 11'h025) ? 14'h1D03 : (reg_pc == 11'h026) ? 14'h283E : (reg_pc == 11'h027) ? 14'h1803 : (reg_pc == 11'h028) ? 14'h283E : (reg_pc == 11'h029) ? 14'h3080 : (reg_pc == 11'h02a) ? 14'h3C60 : (reg_pc == 11'h02b) ? 14'h00A1 : (reg_pc == 11'h02c) ? 14'h30E0 : (reg_pc == 11'h02d) ? 14'h0621 : (reg_pc == 11'h02e) ? 14'h1C03 : (reg_pc == 11'h02f) ? 14'h283E : (reg_pc == 11'h030) ? 14'h1C03 : (reg_pc == 11'h031) ? 14'h283E : (reg_pc == 11'h032) ? 14'h3020 : (reg_pc == 11'h033) ? 14'h3C80 : (reg_pc == 11'h034) ? 14'h00A1 : (reg_pc == 11'h035) ? 14'h3060 : (reg_pc == 11'h036) ? 14'h0621 : (reg_pc == 11'h037) ? 14'h1D03 : (reg_pc == 11'h038) ? 14'h283E : (reg_pc == 11'h039) ? 14'h1803 : (reg_pc == 11'h03a) ? 14'h283E : (reg_pc == 11'h03b) ? 14'h30FF : (reg_pc == 11'h03c) ? 14'h0086 : (reg_pc == 11'h03d) ? 14'h2840 : (reg_pc == 11'h03e) ? 14'h3000 : (reg_pc == 11'h03f) ? 14'h0086 : (reg_pc == 11'h040) ? 14'h2840 : 14'h0000; reg ins_fatch; always @ (posedge clk) begin if(ins_fatch) ibus_d <= ins_rom; end // Create FILE register RAM block here // Create 0x20 bytes starting at address 0x20 reg [7:0] file_ram [0:31]; always @ (posedge clk) begin fbus_do <= file_ram[fbus_a - 7'h20]; if(fbus_wr) begin file_ram[fbus_a - 7'h20] <= fbus_di; end end //****************************************************** //** Special function registers // Status register reg [7:0] reg_status; assign flag_z = reg_status[2]; assign flag_c = reg_status[0]; reg [7:0] reg_w; //****************************************************** //** Instruction decoder // reg [6:0] dec_freg_a; // Address of FILE REG reg dec_to_w; // Output to W reg reg dec_to_f; // Output to FILE reg reg [10:0] dec_pc_d; // What to write to PC reg reg dec_to_pc; // Output to PC reg reg dec_to_z; // Update Z Flag? reg dec_to_c; // Update C Flag? reg [7:0] dec_l; // Decoded literal data reg dec_from_f; // ALU to use File input reg dec_from_l; // ALU to use literal input reg dec_from_w; // ALU to use W input reg dec_btfsc; // BTFSC reg dec_btfss; // BTFSS reg [2:0] dec_bit; // Bit extraction reg dec_add; // Add operation reg dec_and; // AND operation reg dec_clear; // Zero output reg dec_inv; // Inversion reg dec_dec; // Decrease by 1 reg dec_inc; // Increase by 1 reg dec_skipz; // Skip next if zero reg dec_or; // OR operation reg dec_xor; // XOR operation reg dec_sub; // Substract operation reg dec_rrl; // Rotate left reg dec_rrr; // Rotate right reg dec_swap; // Swap the upper/lower nibble reg dec_bsf; // Bit set reg dec_bcf; // Bit clear reg dec_to_stack; // Store into stack reg dec_from_stack; // Pop the stack always @ * begin dec_freg_a = 0; dec_pc_d = 0; dec_l = 0; dec_bit = 0; dec_to_pc = 0; dec_to_z = 0; dec_to_c = 0; dec_to_w = 0; dec_to_f = 0; dec_from_f = 0; dec_from_l = 0; dec_from_w = 0; dec_btfsc = 0; dec_btfss = 0; dec_add = 0; dec_and = 0; dec_clear = 0; dec_inv = 0; dec_dec = 0; dec_inc = 0; dec_skipz = 0; dec_or = 0; dec_xor = 0; dec_sub = 0; dec_rrl = 0; dec_rrr = 0; dec_swap = 0; dec_bsf = 0; dec_bcf = 0; dec_to_stack = 0; dec_from_stack = 0; casez(ibus_d) 14'b000111????????: // ADDWF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_from_w = 1; dec_to_z = 1; dec_to_c = 1; dec_add = 1; end 14'b000101????????: // ANDWF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_from_w = 1; dec_to_z = 1; dec_and = 1; end 14'b0000011???????: // CLRF begin dec_to_f = 1; dec_freg_a = ibus_d[6:0]; dec_to_z = 1; dec_clear = 1; end 14'b00000100000011: // CLRW begin dec_to_w = 1; dec_to_z = 1; dec_clear = 1; end 14'b001001????????: // COMF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_to_z = 1; dec_inv = 1; end 14'b000011????????: // DECF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_to_z = 1; dec_dec = 1; end 14'b001010????????: // INCF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_to_z = 1; dec_inc = 1; end 14'b001011????????: // DECFSZ begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_dec = 1; dec_skipz = 1; end 14'b001111????????: // INCFSZ begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_inc = 1; dec_skipz = 1; end 14'b000100????????: // IORWF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_from_w = 1; dec_or = 1; dec_to_z = 1; end 14'b001101????????: // RLF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_rrl = 1; dec_to_c = 1; end 14'b001100????????: // RRF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_rrr = 1; dec_to_c = 1; end 14'b000010????????: // SUBWF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_from_w = 1; dec_sub = 1; dec_to_c = 1; dec_to_z = 1; end 14'b000110????????: // XORWF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_from_w = 1; dec_xor = 1; dec_to_z = 1; end 14'b001110????????: // SWAPF begin dec_to_w = !ibus_d[7]; // d=0 to W dec_to_f = ibus_d[7]; // d=1 to F dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_swap = 1; end 14'b001000????????: // MOVF begin dec_to_w = !ibus_d[7]; dec_to_f = ibus_d[7]; dec_freg_a = ibus_d[6:0]; dec_from_f = 1; dec_to_z = 1; // Update Z flag end 14'b0000001???????: // MOVWF begin dec_to_f = 1; dec_from_w = 1; dec_freg_a = ibus_d[6:0]; end 14'b0110??????????: // BTFSC begin dec_btfsc = 1; dec_from_f = 1; dec_freg_a = ibus_d[6:0]; dec_bit = ibus_d[9:7]; end 14'b0111??????????: // BTFSS begin dec_btfss = 1; dec_from_f = 1; dec_freg_a = ibus_d[6:0]; dec_bit = ibus_d[9:7]; end 14'b0101??????????: // BSF begin dec_bsf = 1; dec_from_f = 1; dec_to_f = 1; dec_freg_a = ibus_d[6:0]; dec_bit = ibus_d[9:7]; end 14'b0100??????????: // BCF begin dec_bcf = 1; dec_from_f = 1; dec_to_f = 1; dec_freg_a = ibus_d[6:0]; dec_bit = ibus_d[9:7]; end 14'b1100??????????: // MOVLW begin dec_to_w = 1; dec_from_l = 1; dec_l = ibus_d[7:0]; end 14'b101???????????: // GOTO begin dec_to_pc = 1; dec_pc_d = ibus_d[10:0]; end 14'b0000000??00000: // NOP begin end 14'b11110?????????: // SUBLW begin dec_l = ibus_d[7:0]; dec_from_l = 1; dec_from_w = 1; dec_sub = 1; dec_to_w = 1; dec_to_c = 1; dec_to_z = 1; end 14'b11111?????????: // ADDLW begin dec_l = ibus_d[7:0]; dec_from_l = 1; dec_from_w = 1; dec_to_z = 1; dec_to_c = 1; dec_to_w = 1; dec_add = 1; end 14'b111001????????: // ANDLW begin dec_l = ibus_d[7:0]; dec_from_l = 1; dec_from_w = 1; dec_to_w = 1; dec_to_z = 1; dec_and = 1; end 14'b111000????????: // IORLW begin dec_l = ibus_d[7:0]; dec_from_l = 1; dec_from_w = 1; dec_to_z = 1; dec_to_c = 1; dec_to_w = 1; dec_or = 1; end 14'b111010????????: // XORLW begin dec_l = ibus_d[7:0]; dec_from_l = 1; dec_from_w = 1; dec_to_z = 1; dec_to_c = 1; dec_to_w = 1; dec_xor = 1; end 14'b100???????????: // CALL begin dec_to_pc = 1; dec_to_stack = 1; dec_pc_d = ibus_d[10:0]; end 14'b00000000001000: // RETURN begin dec_from_stack = 1; end 14'b1101??????????: // RETLW begin dec_l = ibus_d[7:0]; dec_from_l = 1; dec_to_w = 1; dec_from_stack = 1; end endcase end //****************************************************** //** Decode special function register locations // reg [7:0] portb; wire [7:0] fbus_i_do; // Intermediate bus assign fbus_i_do = (fbus_a[6:0] == 7'h03) ? reg_status : (fbus_a[6:0] == 7'h06) ? portb : (fbus_a[6:0] == 7'h05) ? porta : fbus_do; always @ (posedge clk) begin if(fbus_wr) begin if(fbus_a[6:0] == 7'h06) portb <= fbus_di; end end //****************************************************** //** ALU // reg [7:0] alu_output; reg alu_z_flag; reg alu_c_flag; reg alu_skip_next; // Bit extraction wire extracted_bit; assign extracted_bit = (dec_bit == 3'h0) ? fbus_i_do[0] : (dec_bit == 3'h1) ? fbus_i_do[1] : (dec_bit == 3'h2) ? fbus_i_do[2] : (dec_bit == 3'h3) ? fbus_i_do[3] : (dec_bit == 3'h4) ? fbus_i_do[4] : (dec_bit == 3'h5) ? fbus_i_do[5] : (dec_bit == 3'h6) ? fbus_i_do[6] : (dec_bit == 3'h7) ? fbus_i_do[7] : 1'b0; wire [7:0] bit_mask = (dec_bit == 3'h0) ? 8'b00000001 : (dec_bit == 3'h1) ? 8'b00000010 : (dec_bit == 3'h2) ? 8'b00000100 : (dec_bit == 3'h3) ? 8'b00001000 : (dec_bit == 3'h4) ? 8'b00010000 : (dec_bit == 3'h5) ? 8'b00100000 : (dec_bit == 3'h6) ? 8'b01000000 : (dec_bit == 3'h7) ? 8'b10000000 : 1'b0; always @ * begin alu_output = 8'bx; alu_c_flag = 1'bx; if(dec_from_f) alu_output = fbus_i_do; if(dec_from_l) alu_output = dec_l; if(dec_from_w) alu_output = reg_w; if(dec_add && dec_from_w) begin if(dec_from_f) {alu_c_flag, alu_output} = {1'b0, fbus_i_do} + {1'b0, reg_w}; else if(dec_from_l) {alu_c_flag, alu_output} = {1'b0, dec_l} + {1'b0, reg_w}; end if(dec_sub && dec_from_w) begin if(dec_from_f) {alu_c_flag, alu_output} = {1'b0, fbus_i_do} - {1'b0, reg_w}; else if(dec_from_l) {alu_c_flag, alu_output} = {1'b0, dec_l} - {1'b0, reg_w}; end if(dec_and && dec_from_w) begin if(dec_from_f) alu_output = fbus_i_do & reg_w; else if(dec_from_l) alu_output = dec_l & reg_w; end if(dec_or && dec_from_w) begin if(dec_from_f) alu_output = fbus_i_do | reg_w; else if(dec_from_l) alu_output = dec_l | reg_w; end if(dec_xor && dec_from_w) begin if(dec_from_f) alu_output = fbus_i_do ^ reg_w; else if(dec_from_l) alu_output = dec_l ^ reg_w; end if(dec_bsf) alu_output = fbus_i_do | bit_mask; if(dec_bcf) alu_output = fbus_i_do & ~bit_mask; if(dec_swap) {alu_output[3:0], alu_output[7:4]} = fbus_i_do; if(dec_rrl) {alu_c_flag, alu_output} = {fbus_i_do, flag_c}; if(dec_rrr) {alu_output, alu_c_flag} = {flag_c, fbus_i_do}; if(dec_dec) alu_output = fbus_i_do - 8'b1; if(dec_inc) alu_output = fbus_i_do + 8'b1; if(dec_inv) alu_output = ~fbus_i_do; if(dec_clear) alu_output = 0; if(dec_btfss && extracted_bit) alu_skip_next = 1; if(dec_btfsc && !extracted_bit) alu_skip_next = 1; // Zero calculation if(alu_output == 8'h00) alu_z_flag = 1; else alu_z_flag = 0; // Longest path, because this depends on z flag if(dec_skipz && alu_z_flag) alu_skip_next = 1; else alu_skip_next = 0; end //****************************************************** //** Execution control // parameter STATE_INIT = 0; parameter STATE_INIT2 = 1; parameter STATE_DECODE = 2; parameter STATE_LOAD = 3; parameter STATE_EXEC = 4; parameter STATE_STORE = 5; parameter STATE_STALE = 6; reg [2:0] exec_state; reg exec_stale; // Stale the next cycle always @ (posedge clk) begin if(reset) begin exec_state <= STATE_INIT; reg_status <= 8'b00000000; reg_w <= 0; fbus_wr <= 0; fbus_a <= 0; exec_stale <= 0; end else begin case (exec_state) STATE_INIT: begin exec_state <= STATE_INIT2; end STATE_INIT2: begin exec_state <= STATE_DECODE; end // Execution states: STATE_DECODE: begin fbus_a[6:0] <= dec_freg_a; exec_state <= STATE_LOAD; end STATE_LOAD: begin if(exec_stale) exec_state <= STATE_STALE; else exec_state <= STATE_EXEC; end STATE_EXEC: begin if(dec_to_w) reg_w <= alu_output; if(dec_to_f) begin fbus_di <= alu_output; fbus_wr <= 1; end if(fbus_a[6:0] == 7'h03 && dec_to_f) reg_status <= alu_output; else begin if(dec_to_z) reg_status[2] <= alu_z_flag; // Z flag if(dec_to_c) reg_status[0] <= alu_c_flag; // C flag end //Don't really need this, GOTO is now 1 cycle //if(dec_to_pc) // exec_stale <= 1; if(alu_skip_next) exec_stale <= 1; exec_state <= STATE_STORE; end STATE_STALE: begin exec_stale <= 0; exec_state <= STATE_STORE; end STATE_STORE: begin fbus_wr <= 0; exec_state <= STATE_DECODE; end endcase end // Reset end //****************************************************** //** PC control // reg [4:0] stack_pt; // Stack reg [10:0] stack_ram [0:15]; always @ (posedge clk) begin if(reset) begin reg_pc <= 0; // Reset PC to zero, start of execution ins_fatch <= 0; stack_pt <= 0; end else begin case (exec_state) STATE_INIT: begin ins_fatch <= 1; end STATE_INIT2: begin ins_fatch <= 0; end STATE_DECODE: begin end STATE_LOAD: begin if(!dec_from_stack) begin if(dec_to_pc && !exec_stale) reg_pc <= dec_pc_d; else reg_pc <= reg_pc + 1; end else begin reg_pc <= stack_ram[stack_pt - 1]; stack_pt <= stack_pt - 1; end if(dec_to_stack) begin stack_ram[stack_pt] <= reg_pc + 1; stack_pt <= stack_pt + 1; end end STATE_EXEC: begin ins_fatch <= 1; end STATE_STALE: begin ins_fatch <= 1; end STATE_STORE: begin ins_fatch <= 0; end endcase end // Reset end endmodule