// // 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 [3:0] ALUOP_ADD = 4'b0000; parameter [3:0] ALUOP_SUB = 4'b1000; parameter [3:0] ALUOP_AND = 4'b0001; parameter [3:0] ALUOP_OR = 4'b0010; parameter [3:0] ALUOP_XOR = 4'b0011; parameter [3:0] ALUOP_COM = 4'b0100; parameter [3:0] ALUOP_ROR = 4'b0101; parameter [3:0] ALUOP_ROL = 4'b0110; parameter [3:0] 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 [1:0] ALUASEL_W = 2'b00, ALUASEL_SBUS = 2'b01, ALUASEL_K = 2'b10, ALUASEL_BD = 2'b11; parameter [1:0] ALUBSEL_W = 2'b00, ALUBSEL_SBUS = 2'b01, ALUBSEL_K = 2'b10, ALUBSEL_1 = 2'b11; // ALU Operation codes. // parameter [3:0] ALUOP_ADD = 4'b0000; parameter [3:0] ALUOP_SUB = 4'b1000; parameter [3:0] ALUOP_AND = 4'b0001; parameter [3:0] ALUOP_OR = 4'b0010; parameter [3:0] ALUOP_XOR = 4'b0011; parameter [3:0] ALUOP_COM = 4'b0100; parameter [3:0] ALUOP_ROR = 4'b0101; parameter [3:0] ALUOP_ROL = 4'b0110; parameter [3:0] 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! // // `timescale 1ns / 10ps 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) ); // Capture some data initial begin $dumpfile ("pic.vcd"); $dumpvars (0, pictest); end // Reset initial begin 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 ("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 ("porta changes to: %h", porta); end always @(portb) begin if (dbg_showportb) $display ("portb changes to: %h", portb); end always @(portc) begin if (dbg_showportc) $display ("portc changes to: %h", 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