My Report about PIC16F84A Microcontroller Implementation on Verilog Hardware Description Language

Note

1. Introduction

2. Verilog HDL Design

2.1 Arithmetic Logic Unit

module alu ( CLK, CB, WE, B, FI, FO, CI, CO, DC, Z );
input CLK;// Clock
input [4:0] CB; // operation code
input WE; // Write enable for W register
input [2:0] B; // bit position
input [7:0] FI; // operand
input CI; // Carry in
output [7:0] FO; // dest bus
output CO; // Carry out
output DC; // Half carry
output Z; // Zero
reg HC; // half carry
reg [7:0] W;
reg [8:0] tmp;
reg [7:0] bit_mask;
wire sub;
assign sub = ( CB == `ISUB );
always @( B )
case( B )
3'b000: bit_mask = 8'b0000_0001;
3'b001: bit_mask = 8'b0000_0010;
3'b010: bit_mask = 8'b0000_0100;
3'b011: bit_mask = 8'b0000_1000;
3'b100: bit_mask = 8'b0001_0000;
3'b101: bit_mask = 8'b0010_0000;
3'b110: bit_mask = 8'b0100_0000;
3'b111: bit_mask = 8'b1000_0000;
default: bit_mask = 8'bxxxx_xxxx; endcase
always @( CB or FI or W or HC or CI or bit_mask or sub )
begin
HC = 1'b0;
casex( CB ) `
IPSW: tmp = { 1'b0, W }; // Pass W register value
`ICLR: tmp = 9'b0_0000_0000; // Clear
`IADD, `ISUB:
begin { HC, tmp[3:0] }= {1'b0,FI[3:0]} + {1'b0, sub? ~W[3:0]:W[3:0]} + sub;
tmp[8:4] = {1'b0,FI[7:4]} + {1'b0, sub? ~W[7:4]:W[7:4]} + HC; end
`IDEC1, `IDEC2: tmp = { 1'b0, FI } - 1 ; // Decrement, Decrement and skip if 0
`IOR : tmp = { 1'b0, FI } | { 1'b0, W } ; // Logical OR
`IAND: tmp = { 1'b0, FI } & { 1'b0, W } ; // Logical AND
`IXOR: tmp = { 1'b0, FI } ^ { 1'b0, W } ; // Logical Exclusive OR
`IPSF: tmp = { 1'b0, FI } ; // Pass FI
`INTF: tmp = { 1'b0, ~FI } ; // Complement FI
`IINC1, `IINC2: tmp = { 1'b0, FI } + 1 ; // Increment, Increment and skip if 0
`IRRF: tmp = {FI[0], CI, FI[7:1]} ; // Rotate Right through Carry
`IRLF: tmp = {FI, CI} ; // Rotate Left through Carry
`ISWP: tmp = {1'b0, FI[3:0], FI[7:4]}; // nibble swap
`IBCF: tmp = {1'b0, FI} & {1'b0,~bit_mask} ; // bit clear
`IBSF: tmp = {1'b0, FI} | {1'b0, bit_mask} ; // bit set
`IBTF: tmp = {1'b0, FI} & {1'b0, bit_mask} ; // bit test
default: tmp = 9'bx_xxxx_xxxx;
end case
end
// FO
assign FO = tmp[7:0] ;
// W Register
always @( posedge CLK )
if( WE ) W <= tmp[7:0] ; // Flag
assign CO = tmp[8] ; // Carry Borrow flag
assign DC = HC ; // Half carry flag
assign Z = (tmp[7:0] == 0) ; // Zero flag
endmodule
`define IPSW 5'b00000 // Pass W
`define ICLR 5'b00001 // Clear
`define ISUB 5'b00010 // Sub
`define IDEC1 5'b00011 // Dec
`define IOR 5'b00100 // Or
`define IAND 5'b00101 // And
`define IXOR 5'b00110 // Xor
`define IADD 5'b00111 // Add
`define IPSF 5'b01000 // Pass F
`define INTF 5'b01001 // Not
`define IINC1 5'b01010 // Inc
`define IDEC2 5'b01011 // Dec
`define IRRF 5'b01100 // Rotate Right with carry
`define IRLF 5'b01101 // Rotate Left with carry
`define ISWP 5'b01110 // Nibble swap
`define IINC2 5'b01111 // Inc
`define IBCF 5'b100?? // Bit Clear F
`define IBSF 5'b101?? // Bit Set F
`define IBTF 5'b11??? // Bit Test F

2.2 Core Input, Output, and Register

// STATUS Register
`define STATUS { IRP, RP, nTO, nPD, Z, DC, C }
// IR
`define IRB IR[ 9:7]
`define IRK IR[ 7:0]
`define IRF IR[ 6:0]
`define IRA IR[10:0]
// Memory Address
`define A_INDF 7'b000_0000
`define A_PCL 7'b000_0010
module pic16core ( CLK, RST, RA, RB );
input CLK;
input RST;
inout [7:0] RA;
inout [7:0] RB;
parameter PROG = "program.mem";// Special Register
reg IRP;
reg [1:0] RP;
reg nTO, nPD, Z, DC, C; // 03 83 // STATUS
reg [7:0] FSR; // 04 84
reg [7:0] PORTA, TRISA; // 05 85
reg [7:0] PORTB, TRISB; // 06 86
reg [4:0] PCLATH; // 0A 8A
reg [7:0] RAM[ 12 : 127 ]; // 0C-7F // DATA bus
reg [7:0] SDATA; // for special register
reg [7:0] RDATA; // for ALU operand
wire [7:0] WDATA; // for ALU result
wire [7:0] DDATA; // for Data RAM Read Data
// Flag data from ALU to Flag register
wire CO, DCO, ZO; // Control Signal
reg NOP_S; // Fetch cancel on CALL, GOTO
reg [4:0] ALU_CB; // ALU control
reg W_W; // Write enable for W register
reg C_W, DC_W, Z_W; // Write enable for Flag register (STATUS[2:0])
reg F_W; // Write enable for Data memory
reg WDT_C; // WDT clear
reg nTO_S, nTO_C; // nTO set and clear
reg nPD_S, nPD_C; // nPD set and clear
reg SLEEP; // Sleep mode
reg SLP_S; // Sleep mode set // Register
reg [12:0] PC; // Program Counter { PCH, PCL }
reg [13:0] IR; // Instruction Register
// Register
reg [12:0] PC; // Program Counter { PCH, PCL }
reg [13:0] IR; // Instruction Register

2.3 Effective Addressing

// Effective Address
wire [ 8 : 0 ] EA;
assign EA = ( `IRF == 0 ) ? { IRP , FSR [7:0] } : { RP , `IRF};

2.4 Program Counter and Return Stack

// Program Counter & Return Stack
reg [ 12:8 ] STK[0:7]; // Return Stack depth 8
reg [ 2:0] STKP; // Return stack pointer 4 bit
reg STK_PU; // Stack Push enable
reg STK_PO; // Stack Pop enable
reg PC_W; // Write enable for CALL, GOTO
// Program Counter
always @( posedge CLK )
if( RST ) PC <= 0; else // RESET
if( PC_W ) PC <= {PCLATH[4:3],IR[10:0] }; else // CALL, GOTO
if( F_W && EA [6:0] == `A_PCL ) PC <= { PCLATH[4:3], WDATA[7:0] }; else // write PCL register if( STK_PO ) PC <= STK[ STKP-1 ]; else // RETURN, RETLW
if( SLEEP || SLP_S ) PC <= PC ; else // SLEEP mode
PC <= PC + 1;
// Return Stack
always @( posedge CLK )
begin
if( RST ) STKP<= 0 ; else // for Empty
if( STK_PU ) begin STK[ STKP ] <= PC ; STKP<= STKP+1; end else // for CALL
if( STK_PO ) STKP<= STKP-1; // for RETxx
end

2.5 Instruction Memory and Register

// Instruction Memory (8k word)
reg [ 13 : 0 ] IMEM[ 0 : 8195 ];
initial
begin
$readmemh( PROG, IMEM );
end
// Instruction Register
always @( posedge CLK )
if( NOP_S || PC_W || STK_PO || RST )
IR <= 14'b00_0000_0000_0000 ; else // if CALL, RET, cond.SKIP
IR <= IMEM[PC] ; // Instruction fetch

2.6 Decode and Control

// Decode & Control
always @( IR or ZO )
begin
ALU_CB=IR[ 12 : 8 ];
F_W=0; W_W=0; Z_W=0; DC_W=0; C_W=0; nTO_S=0; nTO_C=0; nPD_S=0; nPD_C=0; STK_PU=0;
STK_PO=0; NOP_S=0; PC_W=0; WDT_C=0; SLP_S=0;
case( IR[ 13 : 12] )
2'b00 :
begin
W_W = ~IR[7] && IR[11:8] != 4'b0000 ;
F_W = IR[7]; //same with W_W = IR[7] && |IR[11:8] meaning bit 8 is 1 and bit 9-12 isn`t 0
case( IR[ 11 : 8 ] )
4'b0000:
case( IR[7] )
1'b0: case( IR[ 6 : 0 ] )
7'b000_1000 : begin NOP_S=1 ; STK_PO=1 ; end // RETURN
// 7'b000_1001: ; // RETFIE
7'b110_0011 : begin nTO_S=1; nPD_S=1; WDT_C=1; SLP_S=1 ; NOP_S=1 ; end // SLEEP
// 7'b110_0100: ; // CLRWDT
default: ; // NOP
endcase
1'b1: ; // MOVWF f
endcase
4'b0001: begin Z_W=1 ; end // CLRW, CLRF
4'b0010: begin C_W=1 ; DC_W=1 ; Z_W=1 ; end // SUBWF
4'b0011: begin Z_W=1 ; end // DECF
4'b0100: begin Z_W=1 ; end // IORWF
4'b0101: begin Z_W=1 ; end // ANDWF
4'b0110: begin Z_W=1 ; end // XORWF
4'b0111: begin C_W=1 ; DC_W=1 ; Z_W=1 ; end // ADDWF
4'b1000: begin Z_W=1 ; end // MOVF
4'b1001: begin Z_W=1 ; end // COMF
4'b1010: begin Z_W=1 ; end // INCF
4'b1011: begin NOP_S= (ZO==1) ? 1 : 0 ; end // DECFSZ
4'b1100: begin C_W=1 ; end // RRF
4'b1101: begin C_W=1 ; end // RLF
4'b1110: ; // SWPF
4'b1111: begin NOP_S=ZO ; end // INCFSZ
endcase //IR
end
2'b01 :
begin
case( IR[ 11 : 10 ] )
2'b00 : F_W = 1 ; // BCF f, b
2'b01 : F_W = 1 ; // BSF f, b
2'b10 : NOP_S=ZO ; // BTFSC f, b
2'b11 : NOP_S=~ZO ; // BTFSS f, b
endcase
end
2'b10 :
begin
PC_W = 1 ; NOP_S = 1 ;
case( IR[ 11 ] )
1'b0 : STK_PU = 1 ; // CALL
1'b1 : ; // GOTO
endcase
end
2'b11 :
begin
W_W= 1 ;
casex( IR[ 11 : 8 ] )
4'b00xx : begin ALU_CB=`IPSF ; end // MOVLW k //--> pass in ALU to W //
4'b01xx : begin ALU_CB=`IPSF ; STK_PO = 1 ; NOP_S = 1; end // RETLW k
4'b1000 : begin ALU_CB=`IOR ; Z_W = 1 ; end // IORLW k
4'b1001 : begin ALU_CB=`IAND ; Z_W = 1 ; end // ANDLW k
4'b1010 : begin ALU_CB=`IXOR ; Z_W = 1 ; end // XORLW k
4'b110x : begin ALU_CB=`ISUB ; C_W=1 ; DC_W=1 ; Z_W = 1 ; end // SUBLW k
4'b111x : begin ALU_CB=`IADD ; C_W=1 ; DC_W=1 ; Z_W = 1 ; end // ADDLW k
endcase
end
endcase
end // always @ ( IR or ZO )

2.7 Special Register

// Special Register
// Write
always @( posedge CLK or posedge RST )
begin
if( RST )
begin
C = 0 ;
DC = 0 ;
Z = 0 ;
IRP = 0 ;
RP = 2'b00 ;
nTO = 1 ; // nTO=1 on Power-on
nPD = 1 ; // nPD=1 on Power-on
FSR = 0 ; PCLATH = 5'b00000 ;
PORTA = 8'b0000_0000 ;
TRISA = 8'b1111_1111 ; // All ports for input
PORTB = 8'b0000_0000 ;
TRISB = 8'b1111_1111 ; // Table page 18-20
// All ports for input
end
else
begin
// STATUS
if( C_W ) C = CO ;
if( DC_W ) DC = DCO ;
if( Z_W ) Z = ZO ;
if( nPD_S ) nPD = 1'b1;
if( nPD_C ) nPD = 1'b0;
if( nTO_S ) nTO = 1'b1;
if( nTO_C ) nTO = 1'b0;
// Register Write
if( F_W ) //pic16_behaviour page 41
casex( EA ) //effective address
// 9'b?0_000_0001: TMR0 = WDATA; // 01 101 Described TMR0 part //find in address table
// 9'b?1_000_0001:`OPTION = WDATA; // 81 181
// 9'b??_000_0010: PCL = WDATA; // 02 82 102 182 Described PC part
9'b??_000_0011:`STATUS = WDATA; // 03 83 103 183
9'b??_000_0100: FSR = WDATA; // 04 84 104 184
9'b00_000_0101: PORTA = WDATA; // 05
9'b01_000_0101: TRISA = WDATA; // 85
9'b?0_000_0110: PORTB = WDATA; // 06 106
9'b?1_000_0110: TRISB = WDATA; // 86 186
// 9'b??_000_0111: ; // 07
// 9'b??_000_1000: ; // 08 EEDATA not implement
// 9'b??_000_1001: ; // 09 EEADR not implement
9'b??_000_1010: PCLATH = WDATA[4:0]; // 0A 8A 10A 18A
// 9'b??_000_1011:`INTCON = WDATA; // 0B 8B 10B 18B
endcase
end
end
// Data RAM (Write)
always @( posedge CLK )
begin
if( F_W && ( EA[6:0] >= 7'b001_1000 ) ) RAM[ EA[6:0] ] <= WDATA ; //12-127 PIC16 behaviour p.46 //light behaviour p.41 //Effective Address only 7bit not 9bit start from 12 //store RAM to WDATA
end
// Selecter for Special Register
always @( IR or PC or EA or IRP or RP or nTO or nPD or Z or DC or C // STATUS
or FSR or RA or TRISA or RB or TRISB or PCLATH )
casex( EA )
// 9'b?0_000_0001: SDATA = TMR0;
// 9'b?1_000_0001: SDATA =`OPTION;
9'b??_000_0010: SDATA = PC[7:0] ; // PCL
9'b??_000_0011: SDATA =`STATUS ; // STATUS
9'b??_000_0100: SDATA = FSR ; // FSR
9'b00_000_0101: SDATA = PORTA ; // RA
9'b01_000_0101: SDATA = TRISA ; // TRISA
9'b?0_000_0110: SDATA = PORTB ; // RB
9'b?1_000_0110: SDATA = TRISB ; // TRISB
9'b??_000_1010: SDATA = {3'b000, PCLATH }; // PCLATH //because SDATA is 9 bit and PCLATH is only 6 bit, we need to add 3 bit before PCLATH // 9'b??_000_1011: SDATA =`INTCON;
default: SDATA = 8'bxxxx_xxxx;
endcase
// Data RAM Read
assign DDATA = RAM[EA[7:0]] ;

2.8 Data Path

// Data selector for ALU operand
always @( IR or EA or DDATA or SDATA )
begin RDATA <= 8'bxxxx_xxxx ;
if( &IR[13:12] ) RDATA <= `IRK ; else //
casex( EA )
9'b??_000_0010: RDATA <= SDATA ; // PCL
9'b??_000_0011: RDATA <= SDATA ; // STATUS
9'b??_000_0100: RDATA <= SDATA ; // FSR
9'b0?_000_0101: RDATA <= SDATA ; // PORTA, TRISA
9'b0?_000_0110: RDATA <= SDATA ; // PORTB, TRISB
9'b??_000_1010: RDATA <= SDATA ; // PCLATH
default: RDATA <= RAM[EA[7:0]] ; // Shared memory
endcase
end

2.9 ALU Initiate

// Execute
alu i_alu ( .CLK(CLK), .CB(ALU_CB), .WE(W_W), .B(`IRB),
.FI(RDATA),
.FO(WDATA),
.CI(C), .CO(CO), .DC(DCO), .Z(ZO) );

2.10 Sleep

// Sleep mode
always @( posedge CLK or posedge RST )
begin
if(RST) SLEEP <= 0; else //0 mean run
if(SLP_S) SLEEP <= 1; //1 mean execute the sleep mode
end

2.11 Tristate Buffer

// Tristate buffer for GPIO
assign RA[0] = ( TRISA[0] ) ? 1'bZ : PORTA[0];
assign RA[1] = ( TRISA[1] ) ? 1'bZ : PORTA[1];
assign RA[2] = ( TRISA[2] ) ? 1'bZ : PORTA[2];
assign RA[3] = ( TRISA[3] ) ? 1'bZ : PORTA[3];
assign RA[4] = ( TRISA[4] ) ? 1'bZ : PORTA[4];
assign RA[5] = ( TRISA[5] ) ? 1'bZ : PORTA[5];
assign RA[6] = ( TRISA[6] ) ? 1'bZ : PORTA[6];
assign RA[7] = ( TRISA[7] ) ? 1'bZ : PORTA[7];
assign RB[0] = ( TRISB[0] ) ? 1'bZ : PORTB[0];
assign RB[1] = ( TRISB[1] ) ? 1'bZ : PORTB[1];
assign RB[2] = ( TRISB[2] ) ? 1'bZ : PORTB[2];
assign RB[3] = ( TRISB[3] ) ? 1'bZ : PORTB[3];
assign RB[4] = ( TRISB[4] ) ? 1'bZ : PORTB[4];
assign RB[5] = ( TRISB[5] ) ? 1'bZ : PORTB[5];
assign RB[6] = ( TRISB[6] ) ? 1'bZ : PORTB[6];
assign RB[7] = ( TRISB[7] ) ? 1'bZ : PORTB[7];
endmodule

3. Simulation

Table 1. Original PIC16 versus this report’s design
Table 1. Original PIC16 versus this report’s design

4. Implementation

5. Reference

Mirrors

this blog contains all my articles licensed under creative commons attribution customized sharealike (cc-by-sa) where you can sell but mention the open one here

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store