2263 lines
66 KiB
Verilog
2263 lines
66 KiB
Verilog
//
|
|
// 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
|