| // |
| // Copyright (c) 1999 Thomas Coonan (tcoonan@mindspring.com) |
| // |
| // This source code is free software; you can redistribute it |
| // and/or modify it in source code form under the terms of the GNU |
| // General Public License as published by the Free Software |
| // Foundation; either version 2 of the License, or (at your option) |
| // any later version. |
| // |
| // This program 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 General Public License for more details. |
| // |
| // You should have received a copy of the GNU General Public License |
| // along with this program; if not, write to the Free Software |
| // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
| // |
| // |
| // SYNTHETIC PIC 2.0 4/23/98 |
| // |
| // This is a synthesizable Microchip 16C57 compatible |
| // microcontroller. This core is not intended as a high fidelity model of |
| // the PIC, but simply a way to offer a simple processor core to people |
| // familiar with the PIC who also have PIC tools. |
| // |
| // pictest.v - top-level testbench (NOT SYNTHESIZABLE) |
| // piccpu.v - top-level synthesizable module |
| // picregs.v - register file instantiated under piccpu |
| // picalu.v - ALU instantiated under piccpu |
| // picidec.v - Instruction Decoder instantiated under piccpu |
| // hex2rom.c - C program used to translate MPLAB's INTEL HEX output |
| // into the Verilog $readmemh compatible file |
| // test*.asm - (note the wildcard..) Several test programs used |
| // to help debug the verilog. I used MPLAB and the simulator |
| // to develop these programs and get the expected results. |
| // Then, I ran them on Verilog-XL where they appeared to |
| // match. |
| // |
| // Copyright, Tom Coonan, '97. |
| // Use freely, but not for resale as is. You may use this in your |
| // own projects as desired. Just don't try to sell it as is! |
| // |
| // |
| module picalu ( |
| op, |
| a, |
| b, |
| y, |
| cin, |
| cout, |
| zout |
| ); |
| |
| input [3:0] op; // ALU Operation |
| input [7:0] a; // 8-bit Input a |
| input [7:0] b; // 8-bit Input b |
| output [7:0] y; // 8-bit Output |
| input cin; |
| output cout; |
| output zout; |
| |
| // Reg declarations for outputs |
| reg cout; |
| reg zout; |
| reg [7:0] y; |
| |
| // Internal declarations |
| reg addercout; // Carry out straight from the adder itself. |
| |
| parameter ALUOP_ADD = 4'b0000; |
| parameter ALUOP_SUB = 4'b1000; |
| parameter ALUOP_AND = 4'b0001; |
| parameter ALUOP_OR = 4'b0010; |
| parameter ALUOP_XOR = 4'b0011; |
| parameter ALUOP_COM = 4'b0100; |
| parameter ALUOP_ROR = 4'b0101; |
| parameter ALUOP_ROL = 4'b0110; |
| parameter ALUOP_SWAP = 4'b0111; |
| |
| |
| always @(a or b or cin or op) begin |
| case (op) // synopsys full_case parallel_case |
| ALUOP_ADD: {addercout, y} <= a + b; |
| ALUOP_SUB: {addercout, y} <= a - b; // Carry out is really "borrow" |
| ALUOP_AND: {addercout, y} <= {1'b0, a & b}; |
| ALUOP_OR: {addercout, y} <= {1'b0, a | b}; |
| ALUOP_XOR: {addercout, y} <= {1'b0, a ^ b}; |
| ALUOP_COM: {addercout, y} <= {1'b0, ~a}; |
| ALUOP_ROR: {addercout, y} <= {a[0], cin, a[7:1]}; |
| ALUOP_ROL: {addercout, y} <= {a[7], a[6:0], cin}; |
| ALUOP_SWAP: {addercout, y} <= {1'b0, a[3:0], a[7:4]}; |
| default: {addercout, y} <= {1'b0, 8'h00}; |
| endcase |
| end |
| |
| always @(y) |
| zout <= (y == 8'h00); |
| |
| always @(addercout or op) |
| if (op == ALUOP_SUB) cout <= ~addercout; // Invert adder's carry to get borrow |
| else cout <= addercout; |
| |
| endmodule |
| // |
| // SYNTHETIC PIC 2.0 4/23/98 |
| // |
| // This is a synthesizable Microchip 16C57 compatible |
| // microcontroller. This core is not intended as a high fidelity model of |
| // the PIC, but simply a way to offer a simple processor core to people |
| // familiar with the PIC who also have PIC tools. |
| // |
| // pictest.v - top-level testbench (NOT SYNTHESIZABLE) |
| // piccpu.v - top-level synthesizable module |
| // picregs.v - register file instantiated under piccpu |
| // picalu.v - ALU instantiated under piccpu |
| // picidec.v - Instruction Decoder instantiated under piccpu |
| // hex2rom.c - C program used to translate MPLAB's INTEL HEX output |
| // into the Verilog $readmemh compatible file |
| // test*.asm - (note the wildcard..) Several test programs used |
| // to help debug the verilog. I used MPLAB and the simulator |
| // to develop these programs and get the expected results. |
| // Then, I ran them on Verilog-XL where they appeared to |
| // match. |
| // |
| // Copyright, Tom Coonan, '97. |
| // Use freely, but not for resale as is. You may use this in your |
| // own projects as desired. Just don't try to sell it as is! |
| // |
| // |
| module piccpu ( |
| clk, |
| reset, |
| paddr, |
| pdata, |
| portain, |
| portbout, |
| portcout, |
| |
| debugw, |
| debugpc, |
| debuginst, |
| debugstatus |
| ); |
| |
| input clk; |
| input reset; |
| output [8:0] paddr; |
| input [11:0] pdata; |
| input [7:0] portain; |
| output [7:0] portbout; |
| output [7:0] portcout; |
| |
| output [7:0] debugw; |
| output [8:0] debugpc; |
| output [11:0] debuginst; |
| output [7:0] debugstatus; |
| |
| // Register declarations for outputs |
| reg [8:0] paddr; |
| reg [7:0] portbout; |
| reg [7:0] portcout; |
| |
| // This should be set to the ROM location where our restart vector is. |
| // As set here, we have 512 words of program space. |
| // |
| parameter RESET_VECTOR = 9'h1FF; |
| |
| parameter INDF_ADDRESS = 3'h0, |
| TMR0_ADDRESS = 3'h1, |
| PCL_ADDRESS = 3'h2, |
| STATUS_ADDRESS = 3'h3, |
| FSR_ADDRESS = 3'h4, |
| PORTA_ADDRESS = 3'h5, |
| PORTB_ADDRESS = 3'h6, |
| PORTC_ADDRESS = 3'h7; |
| |
| // Experimental custom peripheral, "Lil Adder (a 4-bit adder)" is at this address. |
| // |
| parameter EXPADDRESS_LILADDER = 7'h7F; |
| |
| // ********* Special internal registers |
| |
| // Instruction Register |
| reg [11:0] inst; |
| |
| // Program Counter |
| reg [8:0] pc; |
| reg [8:0] pcplus1; // Output of the pc incrementer. |
| |
| // Stack Registers and Stack "levels" register. |
| reg [ 1:0] stacklevel; |
| reg [ 8:0] stack1; |
| reg [ 8:0] stack2; |
| |
| // W Register |
| reg [ 7:0] w; |
| |
| // The STATUS register (#3) is 8 bits wide, however, we only currently use 2 bits |
| // of it; the C and Z bit. |
| // |
| // bit 0 - C |
| // bit 2 - Z |
| // |
| reg [ 7:0] status; |
| |
| // The FSR register is the pointer register used for Indirect addressing (e.g. using INDF). |
| reg [ 7:0] fsr; |
| |
| // Timer 0 |
| reg [ 7:0] tmr0; |
| reg [ 7:0] prescaler; |
| |
| // Option Register |
| reg [7:0] option; |
| |
| // Tristate Control registers. We do not neccessarily support bidirectional ports, but |
| // will save a place for these registers and the TRIS instruction. Use for debug. |
| reg [7:0] trisa; |
| reg [7:0] trisb; |
| reg [7:0] trisc; |
| |
| // I/O Port registers |
| // |
| reg [7:0] porta; // Input PORT |
| reg [7:0] portb; // Output PORT |
| reg [7:0] portc; // Output PORT |
| |
| // ********** Instruction Related signals ****** |
| |
| reg skip; // When HI force a NOP (all zeros) into inst |
| |
| reg [2:0] pcinsel; |
| |
| // Derive special sub signals from inst register |
| wire [ 7:0] k; |
| wire [ 4:0] fsel; |
| wire [ 8:0] longk; |
| wire d; |
| wire [ 2:0] b; |
| |
| // ********** File Address ************ |
| // |
| // This is the 7-bit Data Address that includes the lower 5-bit fsel, the |
| // FSR bits and any indirect addressing. |
| // Use this bus to address the Register File as well as all special registers, etc. |
| // |
| reg [6:0] fileaddr; |
| |
| // Address Selects |
| reg specialsel; |
| reg regfilesel; |
| reg expsel; |
| |
| // ****** Instruction Decoder Outputs ************** |
| |
| // Write enable for the actual ZERO and CARRY bits within the status register. |
| // Generated by the Instruction Decoder. |
| // |
| wire [1:0] aluasel; |
| wire [1:0] alubsel; |
| wire [3:0] aluop; |
| |
| wire zwe; |
| wire cwe; |
| |
| wire isoption; |
| wire istris; |
| |
| wire fwe; // High if any "register" is being written to at all. |
| wire wwe; // Write Enable for the W register. Produced by Instruction Decoder. |
| |
| // ************* Internal Busses, mux connections, etc. ******************** |
| |
| // Bit decoder bits. |
| reg [7:0] bd; // Final decoder value after any polarity inversion. |
| reg [7:0] bdec; // e.g. "Bit Decoded" |
| |
| // Data in and out of the and out of the register file |
| // |
| reg [7:0] regfilein; // Input into Register File, is connected to the dbus. |
| wire [7:0] regfileout; // Path leaving the register file, goes to SBUS Mux |
| reg regfilewe; // Write Enable |
| reg regfilere; // Read Enable |
| |
| // |
| // The dbus (Destination Bus) comes out of the ALU. It is available to all |
| // writable registers. |
| // |
| // The sbus (Source Bus) comes from all readable registers as well as the output |
| // of the Register File. It is one of the primary inputs into the ALU muxes. |
| // |
| // The ebus (Expansion Bus) comes from any of our custom modules. They must |
| // all coordinate to place whoever's data onto ebus. |
| // |
| reg [7:0] dbus; |
| reg [7:0] sbus; |
| reg [7:0] ebus; |
| |
| |
| // ALU Signals |
| // |
| reg [7:0] alua; |
| reg [7:0] alub; |
| wire [7:0] aluout; |
| wire alucin; |
| wire alucout; |
| wire aluz; |
| |
| // ALU A and B mux selects. |
| // |
| parameter ALUASEL_W = 2'b00, |
| ALUASEL_SBUS = 2'b01, |
| ALUASEL_K = 2'b10, |
| ALUASEL_BD = 2'b11; |
| |
| parameter ALUBSEL_W = 2'b00, |
| ALUBSEL_SBUS = 2'b01, |
| ALUBSEL_K = 2'b10, |
| ALUBSEL_1 = 2'b11; |
| |
| // ALU Operation codes. |
| // |
| parameter ALUOP_ADD = 4'b0000; |
| parameter ALUOP_SUB = 4'b1000; |
| parameter ALUOP_AND = 4'b0001; |
| parameter ALUOP_OR = 4'b0010; |
| parameter ALUOP_XOR = 4'b0011; |
| parameter ALUOP_COM = 4'b0100; |
| parameter ALUOP_ROR = 4'b0101; |
| parameter ALUOP_ROL = 4'b0110; |
| parameter ALUOP_SWAP = 4'b0111; |
| |
| |
| // Instantiate each of our subcomponents |
| // |
| picregs regs ( |
| .clk (clk), |
| .reset (reset), |
| .we (regfilewe), |
| .re (regfilere), |
| .bank (fileaddr[6:5]), |
| .location (fileaddr[4:0]), |
| .din (regfilein), |
| .dout (regfileout) |
| ); |
| |
| // Instatiate the ALU. |
| // |
| picalu alu ( |
| .op (aluop), |
| .a (alua), |
| .b (alub), |
| .y (aluout), |
| .cin (status[0]), |
| .cout (alucout), |
| .zout (aluz) |
| ); |
| |
| // Instantiate the Instruction Decoder. This is really just a lookup table. |
| // Given the instruction, generate all the signals we need. |
| // |
| // For example, each instruction implies a specific ALU operation. Some of |
| // these are obvious such as the ADDW uses an ADD alu op. Less obvious is |
| // that a mov instruction uses an OR op which lets us do a simple copy. |
| // |
| // Data has to funnel through the ALU, which sometimes makes for contrived |
| // ALU ops. |
| // |
| picidec idec ( |
| .inst (inst), |
| .aluasel (aluasel), |
| .alubsel (alubsel), |
| .aluop (aluop), |
| .wwe (wwe), |
| .fwe (fwe), |
| .zwe (zwe), |
| .cwe (cwe), |
| .bdpol (bdpol), |
| .option (isoption), |
| .tris (istris) |
| ); |
| |
| // *********** Debug **************** |
| assign debugw = w; |
| assign debugpc = pc; |
| assign debuginst = inst; |
| assign debugstatus = status; |
| |
| // *********** REGISTER FILE Addressing **************** |
| // |
| // We implement the following: |
| // - The 5-bit fsel address is within a "BANK" which is 32 bytes. |
| // - The FSR bits 6:5 are the BANK select, so there are 4 BANKS, a |
| // total of 128 bytes. Minus the 8 special registers, that's |
| // really 120 bytes. |
| // - The INDF register is for indirect addressing. Referencing INDF |
| // uses FSR as the pointer. Therefore, using INDF/FSR you address |
| // 7-bits of memory. |
| // We DO NOT currently implement the PAGE for program (e.g. STATUS register |
| // bits 6:5) |
| // |
| // The fsel address *may* be zero in which case, we are to do indirect |
| // addressing, using FSR register as the 8-bit pointer. |
| // |
| // Otherwise, use the 5-bits of FSEL (from the Instruction itself) and |
| // 2 bank bits from the FSR register (bits 6:5). |
| // |
| always @(fsel or fsr) begin |
| if (fsel == INDF_ADDRESS) begin |
| // The INDEX register is addressed. There really is no INDEX register. |
| // Use the FSR as an index, e.g. the FSR contents are the fsel. |
| // |
| fileaddr <= fsr[6:0]; |
| end |
| else begin |
| // Use FSEL field and status bank select bits |
| // |
| fileaddr <= {fsr[6:5], fsel}; |
| end |
| end |
| |
| // Write Enable to Register File. |
| // Assert this when the general fwe (write enable to *any* register) is true AND Register File |
| // address range is specified. |
| // |
| always @(regfilesel or fwe) |
| regfilewe <= regfilesel & fwe; |
| |
| // Read Enable (this if nothing else, helps in debug.) |
| // Assert if Register File address range is specified AND the ALU is actually using some |
| // data off the SBUS. |
| // |
| always @(regfilesel or aluasel or alubsel) |
| regfilere <= regfilesel & ((aluasel == ALUASEL_SBUS) | (alubsel == ALUBSEL_SBUS)); |
| |
| // *********** Address Decodes ************** |
| // |
| // Generate 3 selects: specialsel, regfilesel and expsel |
| always @(fileaddr) begin |
| casex (fileaddr) |
| 7'bXX00XXX: // The SPECIAL Registers are lower 8 addresses, in ALL BANKS |
| begin |
| specialsel <= 1'b1; |
| regfilesel <= 1'b0; |
| expsel <= 1'b0; |
| end |
| 7'b1111111: // EXPANSION Registers are the top (1) addresses |
| begin |
| specialsel <= 1'b0; |
| regfilesel <= 1'b0; |
| expsel <= 1'b1; |
| end |
| default: // Anything else must be in the Register File |
| begin |
| specialsel <= 1'b0; |
| regfilesel <= 1'b1; |
| expsel <= 1'b0; |
| end |
| endcase |
| end |
| |
| // *********** SBUS ************** |
| // The sbus (Source Bus) is the output of a multiplexor that takes |
| // inputs from the Register File, and all other special registers |
| // and input ports. The Source Bus then, one of the inputs to the ALU |
| |
| |
| // First MUX selects from all the special regsiters |
| // |
| always @(fsel or fsr or tmr0 or pc or status |
| or porta or portb or portc or regfileout or ebus |
| or specialsel or regfilesel or expsel) begin |
| |
| // For our current mix of registers and peripherals, only the first 8 addresses |
| // are "special" registers (e.g. not in the Register File). As more peripheral |
| // registers are added, they must be muxed into this MUX as well. |
| // |
| // We currently prohibit tristates. |
| // |
| // |
| if (specialsel) begin |
| // Special register |
| case (fsel[2:0]) // synopsys parallel_case full_case |
| 3'h0: sbus <= fsr; |
| 3'h1: sbus <= tmr0; |
| 3'h2: sbus <= pc[7:0]; |
| 3'h3: sbus <= status; |
| 3'h4: sbus <= fsr; |
| 3'h5: sbus <= porta; // PORTA is an input-only port |
| 3'h6: sbus <= portb; // PORTB is an output-only port |
| 3'h7: sbus <= portc; // PORTC is an output-only port |
| endcase |
| end |
| else begin |
| // |
| // Put whatever address equation is neccessary here. Remember to remove unnecessary |
| // memory elements from Register File (picregs.v). It'll still work, but they'd be |
| // wasted flip-flops. |
| // |
| if (expsel) begin |
| sbus <= ebus; |
| end |
| else begin |
| if (regfilesel) begin |
| // Final Priority is Choose the register file |
| sbus <= regfileout; |
| end |
| else begin |
| sbus <= 8'h00; |
| end |
| end |
| end |
| end |
| |
| // ************** DBUS ****** |
| // The Destination bus is just the output of the ALU. |
| // |
| always @(aluout) |
| dbus <= aluout; |
| |
| always @(dbus) |
| regfilein <= dbus; |
| |
| // Drive the ROM address bus straight from the PC |
| // |
| always @(pc) |
| paddr = pc; |
| |
| |
| // Define sub-signals out of inst |
| // |
| assign k = inst[7:0]; |
| assign fsel = inst[4:0]; |
| assign longk = inst[8:0]; |
| assign d = inst[5]; |
| assign b = inst[7:5]; |
| |
| // Bit Decoder. |
| // |
| // Simply take the 3-bit b field in the PIC instruction and create the |
| // expanded 8-bit decoder field, which is used as a mask. |
| // |
| |
| |
| always @(b) begin |
| case (b) |
| 3'b000: bdec <= 8'b00000001; |
| 3'b001: bdec <= 8'b00000010; |
| 3'b010: bdec <= 8'b00000100; |
| 3'b011: bdec <= 8'b00001000; |
| 3'b100: bdec <= 8'b00010000; |
| 3'b101: bdec <= 8'b00100000; |
| 3'b110: bdec <= 8'b01000000; |
| 3'b111: bdec <= 8'b10000000; |
| endcase |
| end |
| |
| always @(bdec or bdpol) |
| bd <= bdec ^ bdpol; |
| |
| // Instruction regsiter usually get the ROM data as its input, but |
| // sometimes for branching, the skip signal must cause a NOP. |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| inst <= 12'h000; |
| end |
| else begin |
| if (skip == 1'b1) begin |
| inst <= 12'b000000000000; // FORCE NOP |
| end |
| else begin |
| inst <= pdata; |
| end |
| end |
| end |
| |
| // SKIP signal. |
| // |
| // We want to insert the NOP instruction for the following conditions: |
| // GOTO,CALL and RETLW instructions (All have inst[11:10] = 2'b10 |
| // BTFSS instruction when aluz is HI ( |
| // BTFSC instruction when aluz is LO |
| // |
| always @(inst or aluz) begin |
| casex ({inst, aluz}) |
| 13'b10??_????_????_?: // A GOTO, CALL or RETLW instructions |
| skip <= 1'b1; |
| |
| 13'b0110_????_????_1: // BTFSC instruction and aluz == 1 |
| skip <= 1'b1; |
| |
| 13'b0111_????_????_0: // BTFSS instruction and aluz == 0 |
| skip <= 1'b1; |
| |
| 13'b0010_11??_????_1: // DECFSZ instruction and aluz == 1 |
| skip <= 1'b1; |
| |
| 13'b0011_11??_????_1: // INCFSZ instruction and aluz == 1 |
| skip <= 1'b1; |
| |
| default: |
| skip <= 1'b0; |
| endcase |
| end |
| |
| // 4:1 Data MUX into alua |
| // |
| // |
| always @(aluasel or w or sbus or k or bd) begin |
| case (aluasel) |
| 2'b00: alua <= w; |
| 2'b01: alua <= sbus; |
| 2'b10: alua <= k; |
| 2'b11: alua <= bd; |
| endcase |
| end |
| |
| // 4:1 Data MUX into alub |
| // |
| // |
| always @(alubsel or w or sbus or k) begin |
| case (alubsel) |
| 2'b00: alub <= w; |
| 2'b01: alub <= sbus; |
| 2'b10: alub <= k; |
| 2'b11: alub <= 8'b00000001; |
| endcase |
| end |
| |
| // W Register |
| always @(posedge clk) begin |
| if (reset) begin |
| w <= 8'h00; |
| end |
| else begin |
| if (wwe) begin |
| w <= dbus; |
| end |
| end |
| end |
| |
| // ************ Writes to various Special Registers (fsel between 0 and 7) |
| |
| // INDF Register (Register #0) |
| // |
| // Not a real register. This is the Indirect Addressing mode register. |
| // See the regfileaddr logic. |
| |
| // TMR0 Register (Register #1) |
| // |
| // Timer0 is currently only a free-running timer clocked by the main system clock. |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| tmr0 <= 8'h00; |
| end |
| else begin |
| // See if the status register is actually being written to |
| if (fwe & specialsel & (fsel == TMR0_ADDRESS)) begin |
| // Yes, so just update the register from the dbus |
| tmr0 <= dbus; |
| end |
| else begin |
| // Mask off the prescaler value based on desired divide ratio. |
| // Whenever this is zero, then that is our divided pulse. Increment |
| // the final timer value when it's zero. |
| // |
| case (option[2:0]) // synopsys parallel_case full_case |
| 3'b000: if (~|(prescaler & 8'b00000001)) tmr0 <= tmr0 + 1; |
| 3'b001: if (~|(prescaler & 8'b00000011)) tmr0 <= tmr0 + 1; |
| 3'b010: if (~|(prescaler & 8'b00000111)) tmr0 <= tmr0 + 1; |
| 3'b011: if (~|(prescaler & 8'b00001111)) tmr0 <= tmr0 + 1; |
| 3'b100: if (~|(prescaler & 8'b00011111)) tmr0 <= tmr0 + 1; |
| 3'b101: if (~|(prescaler & 8'b00111111)) tmr0 <= tmr0 + 1; |
| 3'b110: if (~|(prescaler & 8'b01111111)) tmr0 <= tmr0 + 1; |
| 3'b111: if (~|(prescaler & 8'b11111111)) tmr0 <= tmr0 + 1; |
| endcase |
| end |
| end |
| end |
| |
| // The prescaler is always counting from 00 to FF |
| always @(posedge clk) begin |
| if (reset) begin |
| prescaler <= 8'h00; |
| end |
| else begin |
| // See if the status register is actually being written to |
| prescaler <= prescaler + 1; |
| end |
| end |
| |
| |
| // PCL Register (Register #2) |
| // |
| // PC Lower 8 bits. This is handled in the PC section below... |
| |
| |
| // STATUS Register (Register #3) |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| status <= 8'h00; |
| end |
| else begin |
| // See if the status register is actually being written to |
| if (fwe & specialsel & (fsel == STATUS_ADDRESS)) begin |
| // Yes, so just update the register from the dbus |
| status <= dbus; |
| end |
| else begin |
| // Update status register on a bit-by-bit basis. |
| // |
| // For the carry and zero flags, each instruction has its own rule as |
| // to whether to update this flag or not. The instruction decoder is |
| // providing us with an enable for C and Z. Use this to decide whether |
| // to retain the existing value, or update with the new alu status output. |
| // |
| status <= { |
| status[7], // BIT 7: Undefined.. (maybe use for debugging) |
| status[6], // BIT 6: Program Page, HI bit |
| status[5], // BIT 5: Program Page, LO bit |
| status[4], // BIT 4: Time Out bit (not implemented at this time) |
| status[3], // BIT 3: Power Down bit (not implemented at this time) |
| (zwe) ? aluz : status[2], // BIT 2: Z |
| status[1], // BIT 1: DC |
| (cwe) ? alucout : status[0] // BIT 0: C |
| }; |
| end |
| end |
| end |
| |
| // FSR Register (Register #4) |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| fsr <= 8'h00; |
| end |
| else begin |
| // See if the status register is actually being written to |
| if (fwe & specialsel & (fsel == FSR_ADDRESS)) begin |
| fsr <= dbus; |
| end |
| end |
| end |
| |
| // OPTION Register |
| // |
| // The special OPTION instruction should move W into the OPTION register. |
| always @(posedge clk) begin |
| if (reset) begin |
| option <= 8'h00; |
| end |
| else begin |
| if (isoption) |
| option <= dbus; |
| end |
| end |
| |
| // PORTA Input Port (Register #5) |
| // |
| // Register anything on the module's porta input on every single clock. |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| porta <= 8'h00; |
| end |
| else begin |
| porta <= portain; |
| end |
| end |
| |
| // PORTB Output Port (Register #6) |
| always @(posedge clk) begin |
| if (reset) begin |
| portb <= 8'h00; |
| end |
| else begin |
| if (fwe & specialsel & (fsel == PORTB_ADDRESS) & ~istris) begin |
| portb <= dbus; |
| end |
| end |
| end |
| |
| // Connect the output ports to the register output. |
| always @(portb) |
| portbout <= portb; |
| |
| // PORTC Output Port (Register #7) |
| always @(posedge clk) begin |
| if (reset) begin |
| portc <= 8'h00; |
| end |
| else begin |
| if (fwe & specialsel & (fsel == PORTC_ADDRESS) & ~istris) begin |
| portc <= dbus; |
| end |
| end |
| end |
| |
| // Connect the output ports to the register output. |
| always @(portc) |
| portcout <= portc; |
| |
| // TRIS Registers |
| always @(posedge clk) begin |
| if (reset) begin |
| trisa <= 8'hff; // Default is to tristate them |
| end |
| else begin |
| if (fwe & specialsel & (fsel == PORTA_ADDRESS) & istris) begin |
| trisa <= dbus; |
| end |
| end |
| end |
| |
| always @(posedge clk) begin |
| if (reset) begin |
| trisb <= 8'hff; // Default is to tristate them |
| end |
| else begin |
| if (fwe & specialsel & (fsel == PORTB_ADDRESS) & istris) begin |
| trisb <= dbus; |
| end |
| end |
| end |
| |
| always @(posedge clk) begin |
| if (reset) begin |
| trisc <= 8'hff; // Default is to tristate them |
| end |
| else begin |
| if (fwe & specialsel & (fsel == PORTC_ADDRESS) & istris) begin |
| trisc <= dbus; |
| end |
| end |
| end |
| |
| |
| // ********** PC AND STACK ************************* |
| // |
| // There are 4 ways to change the PC. They are: |
| // GOTO 101k_kkkk_kkkk |
| // CALL 1001_kkkk_kkkk |
| // RETLW 1000_kkkk_kkkk |
| // MOVF 0010_0010_0010 (e.g. a write to reg #2) |
| // |
| // Remember that the skip instructions work by inserting |
| // a NOP instruction or not into program stream and don't |
| // change the PC. |
| // |
| |
| // We need pc + 1 in several places, so lets define this incrementor and |
| // its output signal it in one place so that we never get redundant adders. |
| // |
| always @(pc) |
| pcplus1 <= pc + 1; |
| |
| parameter PC_SELECT_PCPLUS1 = 3'b000, |
| PC_SELECT_K = 3'b001, |
| PC_SELECT_STACK1 = 3'b010, |
| PC_SELECT_STACK2 = 3'b011, |
| PC_SELECT_DBUS = 3'b100, |
| PC_SELECT_RESET_VECTOR = 3'b101; |
| |
| // 8:1 Data MUX into PC |
| always @(posedge clk) begin |
| case (pcinsel) // synopsys parallel_case full_case |
| 3'b000: pc <= pcplus1; |
| 3'b001: pc <= k; |
| 3'b010: pc <= stack1; |
| 3'b011: pc <= stack2; |
| 3'b100: pc <= dbus; |
| 3'b101: pc <= RESET_VECTOR; |
| |
| // Don't really carry about these... |
| 3'b110: pc <= pc; |
| 3'b111: pc <= pc; |
| endcase |
| end |
| |
| // Select for the MUX going into the PC. |
| // |
| // |
| always @(inst or stacklevel or reset) begin |
| if (reset == 1'b1) begin |
| pcinsel <= PC_SELECT_RESET_VECTOR; |
| end |
| else begin |
| casex ({inst, stacklevel}) |
| 14'b101?_????_????_??: pcinsel <= PC_SELECT_K; // GOTO |
| 14'b1001_????_????_??: pcinsel <= PC_SELECT_K; // CALL |
| 14'b1000_????_????_00: pcinsel <= PC_SELECT_STACK1; // RETLW |
| 14'b1000_????_????_01: pcinsel <= PC_SELECT_STACK1; // RETLW |
| 14'b1000_????_????_10: pcinsel <= PC_SELECT_STACK2; // RETLW |
| 14'b1000_????_????_11: pcinsel <= PC_SELECT_STACK2; // RETLW |
| 14'b0010_0010_0010_??: pcinsel <= PC_SELECT_DBUS; // MOVF where f=PC |
| default: |
| pcinsel <= PC_SELECT_PCPLUS1; |
| endcase |
| end |
| end |
| |
| |
| // Implement STACK1 and STACK2 registers |
| // |
| // The Stack registers are only fed from the PC itself! |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| stack1 <= 9'h000; |
| end |
| else begin |
| // CALL instruction |
| if (inst[11:8] == 4'b1001) begin |
| case (stacklevel) |
| 2'b00: |
| // No previous CALLs |
| begin |
| stack1 <= pc; |
| $display ("Write to STACK1: %0h", pc); |
| end |
| 2'b01: |
| // ONE previous CALL |
| begin |
| stack2 <= pc; |
| $display ("Write to STACK2: %0h", pc); |
| end |
| 2'b10: |
| // TWO previous CALLs -- This is illegal on the 16C5X! |
| begin |
| $display ("Too many CALLs!!"); |
| end |
| 2'b11: |
| begin |
| $display ("Too many CALLs!!"); |
| end |
| endcase |
| end |
| end |
| end |
| |
| // Write to stacklevel |
| // |
| // The stacklevel register keeps track of the current stack depth. On this |
| // puny processor, there are only 2 levels (you could fiddle with this and |
| // increase the stack depth). There are two stack registers, stack1 and stack2. |
| // The stack1 register is used first (e.g. the first time a call is performed), |
| // then stack2. As CALLs are done, stacklevel increments. Conversely, as |
| // RETs are done, stacklevel goes down. |
| |
| always @(posedge clk) begin |
| if (reset == 1'b1) begin |
| stacklevel <= 2'b00; // On reset, there should be no CALLs in progress |
| end |
| else begin |
| casex ({inst, stacklevel}) |
| // Call instructions |
| 14'b1001_????_????_00: stacklevel <= 2'b01; // Record 1st CALL |
| 14'b1001_????_????_01: stacklevel <= 2'b10; // Record 2nd CALL |
| 14'b1001_????_????_10: stacklevel <= 2'b10; // Already 2! Ignore |
| 14'b1001_????_????_11: stacklevel <= 2'b00; // {shouldn't happen} |
| |
| // Return instructions |
| 14'b1000_????_????_00: stacklevel <= 2'b00; // {shouldn't happen} |
| 14'b1000_????_????_01: stacklevel <= 2'b00; // Go back to no CALL in progress |
| 14'b1000_????_????_10: stacklevel <= 2'b01; // Go back to 1 CALL in progress |
| 14'b1000_????_????_11: stacklevel <= 2'b10; // {shouldn't happen} sort of like, Go back to 2 calls in progress |
| default: |
| stacklevel <= stacklevel; |
| endcase |
| end |
| end |
| |
| |
| |
| // ************ EXPANSION ************************* |
| // |
| // The following is an example of customization. |
| // |
| // Example: Create a read/write port located at address 7F. It'll be 8-bits, where |
| // the upper 4 bits are outputs and the lower 4 bits are inputs. |
| // Use indirect addressing to access it (INDF/FSR). Just for fun, let's |
| // attach a special loop-back circuit between the outputs and inputs. |
| // Let's attach... say... a 4-bit adder. |
| // |
| |
| reg [3:0] special_peripheral_writeable_bits; |
| reg [3:0] special_peripheral_readeable_bits; |
| |
| // Implement the writeable bits. |
| // |
| always @(posedge clk) begin |
| if (reset) begin |
| special_peripheral_writeable_bits <= 4'b0000; |
| end |
| else begin |
| if (fwe & expsel & (fileaddr == EXPADDRESS_LILADDER)) begin |
| special_peripheral_writeable_bits <= dbus; |
| end |
| end |
| end |
| |
| // Implement the special peripheral function (the 4-bit adder for this example). |
| always @(special_peripheral_writeable_bits) begin |
| special_peripheral_readeable_bits <= special_peripheral_writeable_bits + 1; |
| end |
| |
| // Drive the ebus. With only one custom address, no more muxing needs to be |
| // done, but if there are multiple custom circuits, everyone needs to cooperate |
| // and drive ebus properly. |
| // |
| always @(fileaddr or special_peripheral_readeable_bits) begin |
| if (fileaddr == EXPADDRESS_LILADDER) |
| ebus <= special_peripheral_readeable_bits; |
| else |
| ebus <= 8'hff; |
| end |
| |
| endmodule |
| // |
| // SYNTHETIC PIC 2.0 4/23/98 |
| // |
| // This is a synthesizable Microchip 16C57 compatible |
| // microcontroller. This core is not intended as a high fidelity model of |
| // the PIC, but simply a way to offer a simple processor core to people |
| // familiar with the PIC who also have PIC tools. |
| // |
| // pictest.v - top-level testbench (NOT SYNTHESIZABLE) |
| // piccpu.v - top-level synthesizable module |
| // picregs.v - register file instantiated under piccpu |
| // picalu.v - ALU instantiated under piccpu |
| // picidec.v - Instruction Decoder instantiated under piccpu |
| // hex2rom.c - C program used to translate MPLAB's INTEL HEX output |
| // into the Verilog $readmemh compatible file |
| // test*.asm - (note the wildcard..) Several test programs used |
| // to help debug the verilog. I used MPLAB and the simulator |
| // to develop these programs and get the expected results. |
| // Then, I ran them on Verilog-XL where they appeared to |
| // match. |
| // |
| // Copyright, Tom Coonan, '97. |
| // Use freely, but not for resale as is. You may use this in your |
| // own projects as desired. Just don't try to sell it as is! |
| // |
| // |
| // This is the PIC Instruction Decoder. |
| // |
| // The 12-bit PIC instruction must result in a set of control |
| // signals to the ALU, register write enables and other wires. |
| // This is purely combinatorial. This can physically be |
| // implemented as a ROM, or, in this implementation a Verilog |
| // casex statement is used to directly synthesize the signals. |
| // This approach is more portable, and hopefully much reduction |
| // occurs in the equations. |
| // |
| // The Synthetic PIC Manual contains a table that better shows |
| // all the required signals per instruction. I basically |
| // took that table and created the Verilog implementation below. |
| // |
| |
| module picidec ( |
| inst, |
| aluasel, |
| alubsel, |
| aluop, |
| wwe, |
| fwe, |
| zwe, |
| cwe, |
| bdpol, |
| option, |
| tris |
| ); |
| |
| input [11:0] inst; |
| |
| output [1:0] aluasel; |
| output [1:0] alubsel; |
| output [3:0] aluop; |
| output wwe; |
| output fwe; |
| output zwe; |
| output cwe; |
| output bdpol; |
| output option; |
| output tris; |
| |
| reg [14:0] decodes; |
| |
| // For reference, the ALU Op codes are: |
| // |
| // ADD 0000 |
| // SUB 1000 |
| // AND 0001 |
| // OR 0010 |
| // XOR 0011 |
| // COM 0100 |
| // ROR 0101 |
| // ROL 0110 |
| // SWAP 0111 |
| |
| assign { aluasel, // Select source for ALU A input. 00=W, 01=SBUS, 10=K, 11=BD |
| alubsel, // Select source for ALU B input. 00=W, 01=SBUS, 10=K, 11="1" |
| aluop, // ALU Operation (see comments above for these codes) |
| wwe, // W register Write Enable |
| fwe, // File Register Write Enable |
| zwe, // Status register Z bit update |
| cwe, // Status register Z bit update |
| bdpol, // Polarity on bit decode vector (0=no inversion, 1=invert) |
| tris, // Instruction is an TRIS instruction |
| option // Instruction is an OPTION instruction |
| } = decodes; |
| |
| // This is a large combinatorial decoder. |
| // I use the casex statement. |
| |
| always @(inst) begin |
| casex (inst) |
| // *** Byte-Oriented File Register Operations |
| // |
| // A A ALU W F Z C B T O |
| // L L O W W W W D R P |
| // U U P E E E E P I T |
| // A B O S |
| // L |
| 12'b0000_0000_0000: decodes <= 15'b00_00_0000_0_0_0_0_0_0_0; // NOP |
| 12'b0000_001X_XXXX: decodes <= 15'b00_00_0010_0_1_0_0_0_0_0; // MOVWF |
| 12'b0000_0100_0000: decodes <= 15'b00_00_0011_1_0_1_0_0_0_0; // CLRW |
| 12'b0000_011X_XXXX: decodes <= 15'b00_00_0011_0_1_1_0_0_0_0; // CLRF |
| 12'b0000_100X_XXXX: decodes <= 15'b01_00_1000_1_0_1_1_0_0_0; // SUBWF (d=0) |
| 12'b0000_101X_XXXX: decodes <= 15'b01_00_1000_0_1_1_1_0_0_0; // SUBWF (d=1) |
| 12'b0000_110X_XXXX: decodes <= 15'b01_11_1000_1_0_1_0_0_0_0; // DECF (d=0) |
| 12'b0000_111X_XXXX: decodes <= 15'b01_11_1000_0_1_1_0_0_0_0; // DECF (d=1) |
| 12'b0001_000X_XXXX: decodes <= 15'b00_01_0010_1_0_1_0_0_0_0; // IORWF (d=0) |
| 12'b0001_001X_XXXX: decodes <= 15'b00_01_0010_0_1_1_0_0_0_0; // IORWF (d=1) |
| 12'b0001_010X_XXXX: decodes <= 15'b00_01_0001_1_0_1_0_0_0_0; // ANDWF (d=0) |
| 12'b0001_011X_XXXX: decodes <= 15'b00_01_0001_0_1_1_0_0_0_0; // ANDWF (d=1) |
| 12'b0001_100X_XXXX: decodes <= 15'b00_01_0011_1_0_1_0_0_0_0; // XORWF (d=0) |
| 12'b0001_101X_XXXX: decodes <= 15'b00_01_0011_0_1_1_0_0_0_0; // XORWF (d=1) |
| 12'b0001_110X_XXXX: decodes <= 15'b00_01_0000_1_0_1_1_0_0_0; // ADDWF (d=0) |
| 12'b0001_111X_XXXX: decodes <= 15'b00_01_0000_0_1_1_1_0_0_0; // ADDWF (d=1) |
| 12'b0010_000X_XXXX: decodes <= 15'b01_01_0010_1_0_1_0_0_0_0; // MOVF (d=0) |
| 12'b0010_001X_XXXX: decodes <= 15'b01_01_0010_0_1_1_0_0_0_0; // MOVF (d=1) |
| 12'b0010_010X_XXXX: decodes <= 15'b01_01_0100_1_0_1_0_0_0_0; // COMF (d=0) |
| 12'b0010_011X_XXXX: decodes <= 15'b01_01_0100_0_1_1_0_0_0_0; // COMF (d=1) |
| 12'b0010_100X_XXXX: decodes <= 15'b01_11_0000_1_0_1_0_0_0_0; // INCF (d=0) |
| 12'b0010_101X_XXXX: decodes <= 15'b01_11_0000_0_1_1_0_0_0_0; // INCF (d=1) |
| 12'b0010_110X_XXXX: decodes <= 15'b01_11_1000_1_0_0_0_0_0_0; // DECFSZ(d=0) |
| 12'b0010_111X_XXXX: decodes <= 15'b01_11_1000_0_1_0_0_0_0_0; // DECFSZ(d=1) |
| 12'b0011_000X_XXXX: decodes <= 15'b01_01_0101_1_0_0_1_0_0_0; // RRF (d=0) |
| 12'b0011_001X_XXXX: decodes <= 15'b01_01_0101_0_1_0_1_0_0_0; // RRF (d=1) |
| 12'b0011_010X_XXXX: decodes <= 15'b01_01_0110_1_0_0_1_0_0_0; // RLF (d=0) |
| 12'b0011_011X_XXXX: decodes <= 15'b01_01_0110_0_1_0_1_0_0_0; // RLF (d=1) |
| 12'b0011_100X_XXXX: decodes <= 15'b01_01_0111_1_0_0_0_0_0_0; // SWAPF (d=0) |
| 12'b0011_101X_XXXX: decodes <= 15'b01_01_0111_0_1_0_0_0_0_0; // SWAPF (d=1) |
| 12'b0011_110X_XXXX: decodes <= 15'b01_11_0000_1_0_0_0_0_0_0; // INCFSZ(d=0) |
| 12'b0011_111X_XXXX: decodes <= 15'b01_11_0000_0_1_0_0_0_0_0; // INCFSZ(d=1) |
| |
| // *** Bit-Oriented File Register Operations |
| // |
| // A A ALU W F Z C B T O |
| // L L O W W W W D R P |
| // U U P E E E E P I T |
| // A B O S |
| // L |
| 12'b0100_XXXX_XXXX: decodes <= 15'b11_01_0001_0_1_0_0_1_0_0; // BCF |
| 12'b0101_XXXX_XXXX: decodes <= 15'b11_01_0010_0_1_0_0_0_0_0; // BSF |
| 12'b0110_XXXX_XXXX: decodes <= 15'b11_01_0001_0_0_0_0_0_0_0; // BTFSC |
| 12'b0111_XXXX_XXXX: decodes <= 15'b11_01_0001_0_0_0_0_0_0_0; // BTFSS |
| |
| // *** Literal and Control Operations |
| // |
| // A A ALU W F Z C B T O |
| // L L O W W W W D R P |
| // U U P E E E E P I T |
| // A B O S |
| // L |
| 12'b0000_0000_0010: decodes <= 15'b00_00_0010_0_1_0_0_0_0_1; // OPTION |
| 12'b0000_0000_0011: decodes <= 15'b00_00_0000_0_0_0_0_0_0_0; // SLEEP |
| 12'b0000_0000_0100: decodes <= 15'b00_00_0000_0_0_0_0_0_0_0; // CLRWDT |
| 12'b0000_0000_0101: decodes <= 15'b00_00_0000_0_1_0_0_0_1_0; // TRIS 5 |
| 12'b0000_0000_0110: decodes <= 15'b00_00_0010_0_1_0_0_0_1_0; // TRIS 6 |
| 12'b0000_0000_0111: decodes <= 15'b00_00_0010_0_1_0_0_0_1_0; // TRIS 7 |
| // |
| // A A ALU W F Z C B T O |
| // L L O W W W W D R P |
| // U U P E E E E P I T |
| // A B O S |
| // L |
| 12'b1000_XXXX_XXXX: decodes <= 15'b10_10_0010_1_0_0_0_0_0_0; // RETLW |
| 12'b1001_XXXX_XXXX: decodes <= 15'b10_10_0010_0_0_0_0_0_0_0; // CALL |
| 12'b101X_XXXX_XXXX: decodes <= 15'b10_10_0010_0_0_0_0_0_0_0; // GOTO |
| 12'b1100_XXXX_XXXX: decodes <= 15'b10_10_0010_1_0_0_0_0_0_0; // MOVLW |
| 12'b1101_XXXX_XXXX: decodes <= 15'b00_10_0010_1_0_1_0_0_0_0; // IORLW |
| 12'b1110_XXXX_XXXX: decodes <= 15'b00_10_0001_1_0_1_0_0_0_0; // ANDLW |
| 12'b1111_XXXX_XXXX: decodes <= 15'b00_10_0011_1_0_1_0_0_0_0; // XORLW |
| |
| default: |
| decodes <= 15'b00_00_0000_0_0_0_0_0_0_0; |
| endcase |
| end |
| |
| endmodule |
| |
| |
| // |
| // SYNTHETIC PIC 2.0 4/23/98 |
| // |
| // This is a synthesizable Microchip 16C57 compatible |
| // microcontroller. This core is not intended as a high fidelity model of |
| // the PIC, but simply a way to offer a simple processor core to people |
| // familiar with the PIC who also have PIC tools. |
| // |
| // pictest.v - top-level testbench (NOT SYNTHESIZABLE) |
| // piccpu.v - top-level synthesizable module |
| // picregs.v - register file instantiated under piccpu |
| // picalu.v - ALU instantiated under piccpu |
| // picidec.v - Instruction Decoder instantiated under piccpu |
| // hex2rom.c - C program used to translate MPLAB's INTEL HEX output |
| // into the Verilog $readmemh compatible file |
| // test*.asm - (note the wildcard..) Several test programs used |
| // to help debug the verilog. I used MPLAB and the simulator |
| // to develop these programs and get the expected results. |
| // Then, I ran them on Verilog-XL where they appeared to |
| // match. |
| // |
| // Copyright, Tom Coonan, '97. |
| // Use freely, but not for resale as is. You may use this in your |
| // own projects as desired. Just don't try to sell it as is! |
| // |
| // |
| |
| //`define DEBUG_SHOWREADS |
| //`define DEBUG_SHOWWRITES |
| |
| // Memory Map: |
| // |
| // PIC Data Memory addressing is complicated. See the Data Book for full explanation.. |
| // |
| // Basically, each BANK contains 32 locations. The lowest 16 locations in ALL Banks |
| // are really all mapped to the same bank (bank #0). The first 8 locations are the Special |
| // registers like the STATUS and PC registers. The upper 16 words in each bank, really are |
| // unique to each bank. The smallest PIC (16C54) only has the one bank #0. |
| // |
| // So, as a programmer, what you get is this. No matter what bank you are in (FSR[6:5]) |
| // you always have access to your special registers and also to registers 8-15. You can |
| // change to a 1 of 4 banks by setting FSR[6:5] and get 4 different sets of registers |
| // 16-31. |
| // |
| // For numbering purposes, I've numbered the registers as if they are one linear memory |
| // space, just like in the Data Book (see Figure 4-15 "Direct/Indirect Addressing"). |
| // So, the unique 16 registers in bank #1 are named r48 - r63 (I use decimal). The |
| // unique registers in bank #3 are therefore r112 - r127. There is no r111 because, |
| // remember, the lower 16 registers each each bank are all reall the same registers 0-15. |
| // |
| // Confused?! The Data Book explains it better than I can. |
| // |
| // bank location |
| // XX 00rrr - The special registers are not implemented in this register file. |
| // XX 01rrr - The 8 common words, just above the Special Regs, same for all Banks |
| // 00 1rrrr - The 16 words unique to Bank #0 |
| // 01 1rrrr - The 16 words unique to Bank #1 |
| // 10 1rrrr - The 16 words unique to Bank #2 |
| // 11 1rrrr - The 16 words unique to Bank #3 |
| // |
| // So, |
| // Special Regs are location[4:3] == 00 |
| // Common Regs are location[4:3] == 01 |
| // Words in banks location[4] == 1 |
| // |
| // |
| // I had problems trying to use simple register file declarations that |
| // would always, always work right, were synthesizable and allowed |
| // me to easily remove locations from the memory space. SOOOooo... I |
| // did the brute force thing and enumerated all the locations.. |
| // |
| // Much larger spaces will probably need a RAM and whatever I do would need |
| // custom hacking anyway.. I don't see an obvious solution to all this, but |
| // welcome suggestions.. |
| // |
| module picregs (clk, reset, we, re, bank, location, din, dout); |
| |
| input clk; |
| input reset; |
| input we; |
| input re; |
| input [1:0] bank; // Bank 0,1,2,3 |
| input [4:0] location; // Location |
| input [7:0] din; // Input |
| output [7:0] dout; // Output |
| |
| //parameter MAX_ADDRESS = 127; |
| |
| reg [7:0] dout; |
| |
| integer index; |
| |
| // Declare the major busses in and out of each block. |
| // |
| reg [7:0] commonblockout; // Data Memory common across all banks |
| reg [7:0] highblock0out; // Upper 16 bytes in BANK #0 |
| reg [7:0] highblock1out; // Upper 16 bytes in BANK #1 |
| reg [7:0] highblock2out; // Upper 16 bytes in BANK #2 |
| reg [7:0] highblock3out; // Upper 16 bytes in BANK #3 |
| |
| reg [7:0] commonblockin; // Data Memory common across all banks |
| reg [7:0] highblock0in; // Upper 16 bytes in BANK #0 |
| reg [7:0] highblock1in; // Upper 16 bytes in BANK #1 |
| reg [7:0] highblock2in; // Upper 16 bytes in BANK #2 |
| reg [7:0] highblock3in; // Upper 16 bytes in BANK #3 |
| |
| reg commonblocksel; // Select |
| reg highblock0sel; // Select |
| reg highblock1sel; // Select |
| reg highblock2sel; // Select |
| reg highblock3sel; // Select |
| // synopsys translate_off |
| integer cycle_counter; |
| initial cycle_counter = 0; |
| always @(negedge clk) begin |
| if (re) begin |
| `ifdef DEBUG_SHOWREADS |
| $display ("[%0d] Read: data = %0h(hex), from bank #%0d(dec) location %0h", cycle_counter, dout, bank, location); |
| `endif |
| end |
| if (we) begin |
| `ifdef DEBUG_SHOWWRITES |
| $display ("[%0d] Write: data = %0h(hex), to bank #%0d(dec) location %0h", cycle_counter, din, bank, location); |
| `endif |
| end |
| if (~reset) cycle_counter = cycle_counter + 1; |
| end |
| // synopsys translate_on |
| |
| // READ the Register File |
| // |
| always @(bank or location or re |
| or commonblockout |
| or highblock0out |
| or highblock1out |
| or highblock2out |
| or highblock3out) begin |
| if (re) begin |
| if (location[4:3] == 2'b01) begin |
| // This is the lower 8 words, common to all banks, just above special registers |
| dout <= commonblockout; // Access to first 8 locations past Special Registers |
| end |
| else begin |
| if (location[4]) begin |
| // Address is in the upper 16 words on one of the 4 banks |
| case (bank) // synopsys full_case parallel_case |
| 2'b00: dout <= highblock0out; // Upper 16 words of Bank #0 |
| 2'b01: dout <= highblock1out; // Upper 16 words of Bank #1 |
| 2'b10: dout <= highblock2out; // Upper 16 words of Bank #2 |
| 2'b11: dout <= highblock3out; // Upper 16 words of Bank #3 |
| endcase |
| end |
| else begin |
| dout <= 8'hff; |
| end |
| end |
| end |
| else begin |
| dout <= 8'hff; |
| end |
| end |
| |
| // Initial Write logic. |
| // |
| // Generate the specific write enables based on the PIC's bank/location rules. |
| // The individual memory blocks will do the actual synchronous write. |
| // |
| always @(we or bank or location or reset) begin |
| if (reset) begin |
| commonblocksel <= 1'b0; |
| highblock0sel <= 1'b0; |
| highblock1sel <= 1'b0; |
| highblock2sel <= 1'b0; |
| highblock3sel <= 1'b0; |
| end |
| else begin |
| if (we) begin |
| if (location[4:3] == 2'b01) begin |
| // This is the lower 8 words, common to all banks, just above special registers |
| commonblocksel <= 1'b1; |
| highblock0sel <= 1'b0; |
| highblock1sel <= 1'b0; |
| highblock2sel <= 1'b0; |
| highblock3sel <= 1'b0; |
| end |
| else begin |
| if (location[4]) begin |
| // Address is in the upper 16 words on one of the 4 banks |
| commonblocksel <= 1'b0; |
| case (bank) // synopsys full_case parallel_case |
| 2'b00: {highblock0sel, highblock1sel, highblock2sel, highblock3sel} <= 4'b1000; // Upper 16 words of Bank #0 |
| 2'b01: {highblock0sel, highblock1sel, highblock2sel, highblock3sel} <= 4'b0100; // Upper 16 words of Bank #1 |
| 2'b10: {highblock0sel, highblock1sel, highblock2sel, highblock3sel} <= 4'b0010; // Upper 16 words of Bank #2 |
| 2'b11: {highblock0sel, highblock1sel, highblock2sel, highblock3sel} <= 4'b0001; // Upper 16 words of Bank #3 |
| endcase |
| end |
| else begin |
| commonblocksel <= 1'b0; |
| highblock0sel <= 1'b0; |
| highblock1sel <= 1'b0; |
| highblock2sel <= 1'b0; |
| highblock3sel <= 1'b0; |
| end |
| end |
| end |
| else begin |
| commonblocksel <= 1'b0; |
| highblock0sel <= 1'b0; |
| highblock1sel <= 1'b0; |
| highblock2sel <= 1'b0; |
| highblock3sel <= 1'b0; |
| end |
| end |
| end |
| |
| // *** Buses feeding the memory blocks are driven directly from din. |
| |
| always @(din) |
| commonblockin <= din; |
| |
| always @(din) |
| highblock0in <= din; |
| |
| always @(din) |
| highblock1in <= din; |
| |
| always @(din) |
| highblock2in <= din; |
| |
| always @(din) |
| highblock3in <= din; |
| |
| // ****************** Common Block ************* |
| |
| reg [7:0] r8, r9, r10, r11, r12, r13, r14, r15; |
| |
| // Read from common block |
| always @(location or |
| r8 or r9 or r10 or r11 or r12 or r13 or r14 or r15) begin |
| case (location[2:0]) |
| 3'h0: commonblockout <= r8; |
| 3'h1: commonblockout <= r9; |
| 3'h2: commonblockout <= r10; |
| 3'h3: commonblockout <= r11; |
| 3'h4: commonblockout <= r12; |
| 3'h5: commonblockout <= r13; |
| 3'h6: commonblockout <= r14; |
| 3'h7: commonblockout <= r15; |
| endcase |
| end |
| |
| // Write to common block |
| always @(posedge clk) begin |
| if (we & commonblocksel) begin |
| case (location[2:0]) |
| 3'h0: r8 <= commonblockin; |
| 3'h1: r9 <= commonblockin; |
| 3'h2: r10 <= commonblockin; |
| 3'h3: r11 <= commonblockin; |
| 3'h4: r12 <= commonblockin; |
| 3'h5: r13 <= commonblockin; |
| 3'h6: r14 <= commonblockin; |
| 3'h7: r15 <= commonblockin; |
| endcase |
| end |
| end |
| |
| // **************** Highblock0 **************** |
| |
| reg [7:0] r16, r17, r18, r19, r20, r21, r22, r23; |
| reg [7:0] r24, r25, r26, r27, r28, r29, r30, r31; |
| |
| // Read from high block bank0 |
| always @(location or |
| r16 or r17 or r18 or r19 or r20 or r21 or r22 or r23 or |
| r24 or r25 or r26 or r27 or r28 or r29 or r30 or r31 |
| ) begin |
| case (location[3:0]) |
| 4'h0: highblock0out <= r16; |
| 4'h1: highblock0out <= r17; |
| 4'h2: highblock0out <= r18; |
| 4'h3: highblock0out <= r19; |
| 4'h4: highblock0out <= r20; |
| 4'h5: highblock0out <= r21; |
| 4'h6: highblock0out <= r22; |
| 4'h7: highblock0out <= r23; |
| 4'h8: highblock0out <= r24; |
| 4'h9: highblock0out <= r25; |
| 4'hA: highblock0out <= r26; |
| 4'hB: highblock0out <= r27; |
| 4'hC: highblock0out <= r28; |
| 4'hD: highblock0out <= r29; |
| 4'hE: highblock0out <= r30; |
| 4'hF: highblock0out <= r31; |
| endcase |
| end |
| |
| // Write to high block bank 0 |
| always @(posedge clk) begin |
| if (we & highblock0sel) begin |
| case (location[3:0]) |
| 4'h0: r16 <= highblock0in; |
| 4'h1: r17 <= highblock0in; |
| 4'h2: r18 <= highblock0in; |
| 4'h3: r19 <= highblock0in; |
| 4'h4: r20 <= highblock0in; |
| 4'h5: r21 <= highblock0in; |
| 4'h6: r22 <= highblock0in; |
| 4'h7: r23 <= highblock0in; |
| 4'h8: r24 <= highblock0in; |
| 4'h9: r25 <= highblock0in; |
| 4'hA: r26 <= highblock0in; |
| 4'hB: r27 <= highblock0in; |
| 4'hC: r28 <= highblock0in; |
| 4'hD: r29 <= highblock0in; |
| 4'hE: r30 <= highblock0in; |
| 4'hF: r31 <= highblock0in; |
| endcase |
| end |
| end |
| |
| // **************** Highblock1 **************** |
| |
| reg [7:0] r48, r49, r50, r51, r52, r53, r54, r55; |
| reg [7:0] r56, r57, r58, r59, r60, r61, r62, r63; |
| |
| // Read |
| always @(location or |
| r48 or r49 or r50 or r51 or r52 or r53 or r54 or r55 or |
| r56 or r57 or r58 or r59 or r60 or r61 or r62 or r63 |
| ) begin |
| case (location[3:0]) |
| 4'h0: highblock1out <= r48; |
| 4'h1: highblock1out <= r49; |
| 4'h2: highblock1out <= r50; |
| 4'h3: highblock1out <= r51; |
| 4'h4: highblock1out <= r52; |
| 4'h5: highblock1out <= r53; |
| 4'h6: highblock1out <= r54; |
| 4'h7: highblock1out <= r55; |
| 4'h8: highblock1out <= r56; |
| 4'h9: highblock1out <= r57; |
| 4'hA: highblock1out <= r58; |
| 4'hB: highblock1out <= r59; |
| 4'hC: highblock1out <= r60; |
| 4'hD: highblock1out <= r61; |
| 4'hE: highblock1out <= r62; |
| 4'hF: highblock1out <= r63; |
| endcase |
| end |
| |
| // Write |
| always @(posedge clk) begin |
| if (we & highblock1sel) begin |
| case (location[3:0]) |
| 4'h0: r48 <= highblock1in; |
| 4'h1: r49 <= highblock1in; |
| 4'h2: r50 <= highblock1in; |
| 4'h3: r51 <= highblock1in; |
| 4'h4: r52 <= highblock1in; |
| 4'h5: r53 <= highblock1in; |
| 4'h6: r54 <= highblock1in; |
| 4'h7: r55 <= highblock1in; |
| 4'h8: r56 <= highblock1in; |
| 4'h9: r57 <= highblock1in; |
| 4'hA: r58 <= highblock1in; |
| 4'hB: r59 <= highblock1in; |
| 4'hC: r60 <= highblock1in; |
| 4'hD: r61 <= highblock1in; |
| 4'hE: r62 <= highblock1in; |
| 4'hF: r63 <= highblock1in; |
| endcase |
| end |
| end |
| |
| |
| // **************** Highblock2 **************** |
| |
| reg [7:0] r80, r81, r82, r83, r84, r85, r86, r87; |
| reg [7:0] r88, r89, r90, r91, r92, r93, r94, r95; |
| |
| // Read |
| always @(location or |
| r80 or r81 or r82 or r83 or r84 or r85 or r86 or r87 or |
| r88 or r89 or r90 or r91 or r92 or r93 or r94 or r95 |
| ) begin |
| case (location[3:0]) |
| 4'h0: highblock2out <= r80; |
| 4'h1: highblock2out <= r81; |
| 4'h2: highblock2out <= r82; |
| 4'h3: highblock2out <= r83; |
| 4'h4: highblock2out <= r84; |
| 4'h5: highblock2out <= r85; |
| 4'h6: highblock2out <= r86; |
| 4'h7: highblock2out <= r87; |
| 4'h8: highblock2out <= r88; |
| 4'h9: highblock2out <= r89; |
| 4'hA: highblock2out <= r90; |
| 4'hB: highblock2out <= r91; |
| 4'hC: highblock2out <= r92; |
| 4'hD: highblock2out <= r93; |
| 4'hE: highblock2out <= r94; |
| 4'hF: highblock2out <= r95; |
| endcase |
| end |
| |
| // Write |
| always @(posedge clk) begin |
| if (we & highblock2sel) begin |
| case (location[3:0]) |
| 4'h0: r80 <= highblock2in; |
| 4'h1: r81 <= highblock2in; |
| 4'h2: r82 <= highblock2in; |
| 4'h3: r83 <= highblock2in; |
| 4'h4: r84 <= highblock2in; |
| 4'h5: r85 <= highblock2in; |
| 4'h6: r86 <= highblock2in; |
| 4'h7: r87 <= highblock2in; |
| 4'h8: r88 <= highblock2in; |
| 4'h9: r89 <= highblock2in; |
| 4'hA: r90 <= highblock2in; |
| 4'hB: r91 <= highblock2in; |
| 4'hC: r92 <= highblock2in; |
| 4'hD: r93 <= highblock2in; |
| 4'hE: r94 <= highblock2in; |
| 4'hF: r95 <= highblock2in; |
| endcase |
| end |
| end |
| |
| // **************** Highblock3 **************** |
| |
| // *** The Following Registers are removed because of CUSTOM Hardware (see piccpu.v) ** |
| // |
| // r129 (or 7E) |
| // |
| // ********** |
| reg [7:0] r112, r113, r114, r115, r116, r117, r118, r119; |
| reg [7:0] r120, r121, r122, r123, r124, r125, r126 /*, r127*/ ; |
| |
| // Read |
| always @(location or |
| r112 or r113 or r114 or r115 or r116 or r117 or r118 or r119 or |
| r120 or r121 or r122 or r123 or r124 or r125 or r126 /* or r127 */ |
| ) begin |
| case (location[3:0]) |
| 4'h0: highblock3out <= r112; |
| 4'h1: highblock3out <= r113; |
| 4'h2: highblock3out <= r114; |
| 4'h3: highblock3out <= r115; |
| 4'h4: highblock3out <= r116; |
| 4'h5: highblock3out <= r117; |
| 4'h6: highblock3out <= r118; |
| 4'h7: highblock3out <= r119; |
| 4'h8: highblock3out <= r120; |
| 4'h9: highblock3out <= r121; |
| 4'hA: highblock3out <= r122; |
| 4'hB: highblock3out <= r123; |
| 4'hC: highblock3out <= r124; |
| 4'hD: highblock3out <= r125; |
| 4'hE: highblock3out <= r126; |
| 4'hF: highblock3out <= 8'hff /* r127*/ ; |
| endcase |
| end |
| |
| // Write |
| always @(posedge clk) begin |
| if (we & highblock3sel) begin |
| case (location[3:0]) |
| 4'h0: r112 <= highblock3in; |
| 4'h1: r113 <= highblock3in; |
| 4'h2: r114 <= highblock3in; |
| 4'h3: r115 <= highblock3in; |
| 4'h4: r116 <= highblock3in; |
| 4'h5: r117 <= highblock3in; |
| 4'h6: r118 <= highblock3in; |
| 4'h7: r119 <= highblock3in; |
| 4'h8: r120 <= highblock3in; |
| 4'h9: r121 <= highblock3in; |
| 4'hA: r122 <= highblock3in; |
| 4'hB: r123 <= highblock3in; |
| 4'hC: r124 <= highblock3in; |
| 4'hD: r125 <= highblock3in; |
| 4'hE: r126 <= highblock3in; |
| 4'hF: /* r127 <= highblock3in */; |
| endcase |
| end |
| end |
| |
| // synopsys translate_off |
| `define CLEAR_MEMORY |
| `ifdef CLEAR_MEMORY |
| initial |
| begin |
| $display ("Clearing SRAM."); |
| clear_memory; |
| end |
| task clear_memory; |
| begin |
| // Common registers |
| r8 = 0; |
| r9 = 0; |
| r10 = 0; |
| r11 = 0; |
| r12 = 0; |
| r13 = 0; |
| r14 = 0; |
| r15 = 0; |
| |
| // Bank #0 |
| r16 = 0; |
| r17 = 0; |
| r18 = 0; |
| r19 = 0; |
| r20 = 0; |
| r21 = 0; |
| r22 = 0; |
| r23 = 0; |
| r24 = 0; |
| r25 = 0; |
| r26 = 0; |
| r27 = 0; |
| r28 = 0; |
| r29 = 0; |
| r30 = 0; |
| r31 = 0; |
| |
| // Bank #1 |
| r48 = 0; |
| r49 = 0; |
| r50 = 0; |
| r51 = 0; |
| r52 = 0; |
| r53 = 0; |
| r54 = 0; |
| r55 = 0; |
| r56 = 0; |
| r57 = 0; |
| r58 = 0; |
| r59 = 0; |
| r60 = 0; |
| r61 = 0; |
| r62 = 0; |
| r63 = 0; |
| |
| // Bank #2 |
| r80 = 0; |
| r94 = 0; |
| |
| // Bank #3 |
| r112 = 0; |
| r126 = 0; |
| |
| end |
| endtask |
| `endif |
| // synopsys translate_on |
| endmodule |
| // |
| // SYNTHETIC PIC 2.0 4/23/98 |
| // |
| // This is a synthesizable Microchip 16C57 compatible |
| // microcontroller. This core is not intended as a high fidelity model of |
| // the PIC, but simply a way to offer a simple processor core to people |
| // familiar with the PIC who also have PIC tools. |
| // |
| // pictest.v - top-level testbench (NOT SYNTHESIZABLE) |
| // piccpu.v - top-level synthesizable module |
| // picregs.v - register file instantiated under piccpu |
| // picalu.v - ALU instantiated under piccpu |
| // picidec.v - Instruction Decoder instantiated under piccpu |
| // hex2rom.c - C program used to translate MPLAB's INTEL HEX output |
| // into the Verilog $readmemh compatible file |
| // test*.asm - (note the wildcard..) Several test programs used |
| // to help debug the verilog. I used MPLAB and the simulator |
| // to develop these programs and get the expected results. |
| // Then, I ran them on Verilog-XL where they appeared to |
| // match. |
| // |
| // Copyright, Tom Coonan, '97. |
| // Use freely, but not for resale as is. You may use this in your |
| // own projects as desired. Just don't try to sell it as is! |
| // |
| // |
| |
| module pictest; |
| |
| // Select which test to run HERE.. |
| parameter TEST_NUMBER = 9; |
| |
| // *** Testing variables |
| // Debug flags. |
| integer dbg_showporta; // Are set in an 'initial' for default values, |
| integer dbg_showportb; // override in specific tests... |
| integer dbg_showportc; // Setting to 1 will cause variable to be displayed. |
| integer dbg_showinst; |
| integer dbg_showrom; |
| integer dbg_showw; |
| integer dbg_showpc; |
| |
| // cycles counter variables |
| integer dbg_showcycles; // Set to 1 to see cycles |
| integer dbg_limitcycles;// Set to one to enable maxcycles check |
| integer dbg_maxcycles; // Limit simulation to some number of cycles. |
| integer cycles; // Cycles counter. |
| |
| |
| |
| // *** Interface to the PICCPU |
| reg clk; |
| reg reset; |
| reg [7:0] porta; |
| wire [7:0] portb; |
| wire [7:0] portc; |
| |
| reg [11:0] rom[0:511]; |
| wire [8:0] romaddr; |
| reg [11:0] romdata; |
| |
| // ROM Interface |
| always @(romaddr) begin |
| romdata = rom[romaddr]; |
| end |
| |
| reg [7:0] last_debugw; |
| reg [8:0] last_debugpc; |
| reg [11:0] last_debuginst; |
| reg [7:0] last_debugstatus; |
| wire [7:0] debugw; |
| wire [8:0] debugpc; |
| wire [11:0] debuginst; |
| wire [7:0] debugstatus; |
| |
| // Instantiate one PICCPU to be tested. |
| piccpu piccpu_inst ( |
| .clk (clk), |
| .reset (reset), |
| .paddr (romaddr), |
| .pdata (romdata), |
| .portain (porta), |
| .portbout (portb), |
| .portcout (portc), |
| .debugw (debugw), |
| .debugpc (debugpc), |
| .debuginst (debuginst), |
| .debugstatus (debugstatus) |
| ); |
| |
| |
| // Reset |
| initial begin |
| $dumpfile("pic.vcd"); |
| $dumpvars(0,pictest); |
| reset = 1; |
| #200; |
| reset = 0; |
| end |
| |
| |
| // Drive the clock input |
| initial begin |
| clk = 0; |
| forever begin |
| #50 clk = 1; |
| #50 clk = 0; |
| end |
| end |
| |
| // Debug defaults. Override in individual test tasks. |
| // |
| initial begin |
| dbg_showporta = 0; |
| dbg_showportb = 0; |
| dbg_showportc = 0; |
| dbg_showinst = 0; |
| dbg_showrom = 0; |
| dbg_showw = 0; |
| dbg_showpc = 0; |
| dbg_showcycles = 0; |
| |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 50000; |
| end |
| |
| // Call the appropriate test task based on the TEST_NUMBER parameter set at top. |
| // |
| initial begin |
| case (TEST_NUMBER) |
| 1: test1; |
| 2: test2; |
| 3: test3; |
| 4: test4; |
| 5: test5; |
| 6: test6; |
| 7: test7; |
| 8: test8; |
| 9: test9; |
| default: |
| begin |
| $display ("ERROR: Unknown Test Number: %0d", TEST_NUMBER); |
| $finish; |
| end |
| endcase |
| end |
| |
| task test1; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #1"); |
| #1; |
| |
| // Only Watch Port B |
| dbg_showportb = 1; |
| dbg_showcycles = 1; |
| |
| $readmemh ("TEST1.ROM",rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test2; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #2"); |
| #1; |
| |
| // Only Watch Port B |
| dbg_showportb = 1; |
| |
| $readmemh ("TEST2.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test3; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #3"); |
| #1; |
| |
| // Only Watch Port B |
| dbg_showportb = 1; |
| |
| $readmemh ("TEST3.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test4; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #4"); |
| #1; |
| |
| // Only Watch Port B |
| dbg_showportb = 1; |
| |
| $readmemh ("TEST4.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test5; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #5"); |
| #1; |
| |
| // Only Watch Port B |
| dbg_showportb = 1; |
| |
| $readmemh ("TEST5.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test6; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #6"); |
| #1; |
| |
| // Watch Port B and C |
| dbg_showportb = 1; |
| dbg_showportc = 1; |
| dbg_limitcycles = 0; |
| |
| $readmemh ("TEST6.ROM", rom); |
| #200; |
| |
| repeat (20) begin |
| porta = $random; |
| #10000; |
| end |
| |
| $finish; |
| end |
| endtask |
| |
| task test7; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #7"); |
| #1; |
| |
| // Only Watch Port B |
| dbg_showportb = 1; |
| |
| $readmemh ("TEST7.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test8; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #8"); |
| #1; |
| |
| // Watch All ports |
| dbg_showporta = 1; |
| dbg_showportb = 1; |
| dbg_showportc = 1; |
| |
| $readmemh ("TEST8.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 500; |
| end |
| endtask |
| |
| task test9; |
| begin |
| $display ("SYNTHETIC PIC 2.0. This is TEST #9"); |
| #1; |
| |
| // Watch All ports |
| dbg_showportb = 1; |
| dbg_showportc = 1; |
| |
| $readmemh ("contrib/TEST9.ROM", rom); |
| dbg_limitcycles = 1; |
| dbg_maxcycles = 2000; |
| end |
| endtask |
| |
| // ******** END Of TEST TASKS |
| |
| // Cycles counter |
| // |
| initial begin |
| cycles = 0; |
| #1; |
| // Don't start counting until after reset. |
| @(negedge reset); |
| |
| forever begin |
| @(posedge clk); |
| cycles = cycles + 1; |
| if ((cycles % 256) == 0) begin |
| if (dbg_showcycles) begin |
| $display ("#Cycles = %0d", cycles); |
| end |
| end |
| |
| if (dbg_limitcycles) begin |
| if (cycles > dbg_maxcycles) begin |
| $display ("Maximum cycles (%0d) Exceeded. Halting simulation.", dbg_maxcycles); |
| $finish; |
| end |
| end |
| end |
| end |
| |
| always @(romaddr) begin |
| if (dbg_showrom) |
| $display ("ROM Address = %h, Data = %h", romaddr, romdata); |
| end |
| |
| always @(porta) begin |
| if (dbg_showporta) |
| $display ("%d: porta changes to: %h", $time, porta); |
| end |
| |
| always @(portb) begin |
| if (dbg_showportb) |
| $display ("%d: portb changes to: %h", $time, portb); |
| end |
| |
| always @(portc) begin |
| if (dbg_showportc) |
| $display ("%d: portc changes to: %h", $time, portc); |
| end |
| |
| initial begin |
| if (dbg_showw) begin |
| forever begin |
| @(negedge clk); |
| if (debugw != last_debugw) begin |
| $display ("W = %0h", debugw); |
| end |
| last_debugw = debugw; |
| end |
| end |
| end |
| |
| initial begin |
| if (dbg_showpc) begin |
| forever begin |
| @(negedge clk); |
| $display ("PC = %0h", debugpc); |
| end |
| end |
| end |
| |
| |
| |
| reg [11:0] last_pc; |
| |
| always @(posedge clk) begin |
| last_pc = debugpc; |
| end |
| |
| initial begin |
| if (dbg_showinst) begin |
| forever begin |
| @(negedge clk); |
| |
| if (debuginst[11:0] == 12'b0000_0000_0000) begin |
| $display ("%h NOP", last_pc); |
| end |
| else if (debuginst[11:5] == 7'b0000_001) begin |
| $display ("%h MOVWF f=0x%0h", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst == 12'b0000_0100_0000) begin |
| $display ("%h CLRW", last_pc); |
| end |
| else if (debuginst[11:5] == 7'b0000_011) begin |
| $display ("%h CLRF f=0x%0h", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0000_10) begin |
| if (piccpu_inst.d == 0) $display ("%h SUBWF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h SUBWF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| |
| else if (debuginst[11:6] == 7'b0000_11) begin |
| if (piccpu_inst.d == 0) $display ("%h DECF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h DECF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0001_00) begin |
| if (piccpu_inst.d == 0) $display ("%h IORWF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h IORWF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0001_01) begin |
| if (piccpu_inst.d == 0) $display ("%h ANDWF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h ANDWF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0001_10) begin |
| if (piccpu_inst.d == 0) $display ("XORWF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h XORWF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0001_11) begin |
| if (piccpu_inst.d == 0) $display ("%h ADDWF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h ADDWF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0010_00) begin |
| if (piccpu_inst.d == 0) $display ("%h MOVF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h MOVF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0010_01) begin |
| if (piccpu_inst.d == 0) $display ("%h COMF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h COMF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0010_10) begin |
| if (piccpu_inst.d == 0) $display ("%h INCF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h INCF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0010_11) begin |
| if (piccpu_inst.d == 0) $display ("%h DECFSZ f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h DECFSZ f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0011_00) begin |
| if (piccpu_inst.d == 0) $display ("%h RRF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h RRF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0011_01) begin |
| if (piccpu_inst.d == 0) $display ("%h RLF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h RLF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0011_10) begin |
| if (piccpu_inst.d == 0) $display ("%h SWAPF f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h SWAPF f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| else if (debuginst[11:6] == 7'b0011_11) begin |
| if (piccpu_inst.d == 0) $display ("%h INCFSZ f=0x%0h, W", last_pc, debuginst[4:0]); |
| else $display ("%h INCFSZ f=0x%0h, f", last_pc, debuginst[4:0]); |
| end |
| |
| // Bit-Oriented File Register Operations |
| else if (debuginst[11:8] == 4'b0100) begin |
| $display ("%h BCF f=0x%0h, bit=%0d", last_pc, debuginst[4:0], piccpu_inst.b); |
| end |
| else if (debuginst[11:8] == 4'b0101) begin |
| $display ("%h BCF f=0x%0h, bit=%0d", last_pc, debuginst[4:0], piccpu_inst.b); |
| end |
| else if (debuginst[11:8] == 4'b0110) begin |
| if (piccpu_inst.skip) $display ("%h BTFSC f=0x%0h, bit=%0d {Will Skip..}", last_pc, debuginst[4:0], piccpu_inst.b); |
| else $display ("%h BTFSC f=0x%0h, bit=%0d {Will NOT Skip..}", last_pc, debuginst[4:0], piccpu_inst.b); |
| end |
| else if (debuginst[11:8] == 4'b0111) begin |
| if (piccpu_inst.skip) $display ("%h BTFSS f=0x%0h, bit=%0d {Will Skip..}", last_pc, debuginst[4:0], piccpu_inst.b); |
| else $display ("%h BTFSS f=0x%0h, bit=%0d {Will NOT Skip..}", last_pc, debuginst[4:0], piccpu_inst.b); |
| end |
| |
| // Literal and Control Operations |
| else if (debuginst[11:0] == 16'b0000_0000_0010) begin |
| $display ("%h OPTION", last_pc); |
| end |
| else if (debuginst[11:0] == 16'b0000_0000_0011) begin |
| $display ("%h SLEEP", last_pc); |
| end |
| else if (debuginst[11:0] == 16'b0000_0000_0100) begin |
| $display ("%h CLRWDT", last_pc); |
| end |
| else if (debuginst[11:3] == 13'b0000_0000_0) begin |
| $display ("%h TRIS, f=0x%0h", last_pc, debuginst[2:0]); |
| end |
| else if (debuginst[11:8] == 4'b1000) begin |
| $display ("%h RETLW, k=0x%0h", last_pc, debuginst[7:0]); |
| end |
| else if (debuginst[11:8] == 4'b1001) begin |
| $display ("%h CALL, k=0x%0h", last_pc, debuginst[7:0]); |
| end |
| else if (debuginst[11:9] == 3'b101) begin |
| $display ("%h GOTO, k=0x%0h", last_pc, debuginst[8:0]); |
| end |
| else if (debuginst[11:8] == 4'b1100) begin |
| $display ("%h MOVLW, k=0x%0h", last_pc, debuginst[7:0]); |
| end |
| else if (debuginst[11:8] == 4'b1101) begin |
| $display ("%h IORLW, k=0x%0h", last_pc, debuginst[7:0]); |
| end |
| else if (debuginst[11:8] == 4'b1110) begin |
| $display ("%h ANDLW, k=0x%0h", last_pc, debuginst[7:0]); |
| end |
| else if (debuginst[11:8] == 4'b1111) begin |
| $display ("%h XORLW, k=0x%0h", last_pc, debuginst[7:0]); |
| end |
| else begin |
| $display ("Hmmm! instruction not recognized?! %0h", debuginst); |
| end |
| end |
| end |
| end |
| |
| |
| endmodule |
| |