iverilog/ivtest/contrib/pic.v

2263 lines
66 KiB
Coq
Raw Normal View History

//
// 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(0);
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