(3)Verilogに基づくRISC CPUテスト
これは、上述したPIC 16 C 57機能設計に基づくCPUシミュレーションテストであり、Vivadoソフトウェアに基づくRTL Simulationモジュールは、アセンブラプログラムの複数のテストプログラムのシミュレーション検証により、その設計の正しさを確認している。
アセンブラは次のようになります.INDF equ H'00' ; Magic register that uses INDIRECT register
TIMER0 equ H'01' ; Timer register
STATUS equ H'03' ; STATUS register F3
FSR equ H'04' ; INDIRECT Pointer Register
porta equ H'05' ; I/O register F5
portb equ H'06' ; I/O register F6
portc equ H'07' ; I/O register F7
x equ H'0A' ; Our general variable
y equ H'0B' ; Another variable..
CARRY equ H'00' ; Carry bit in STATUS register
ZERO equ H'02' ; Zero bit in STATUS register
W equ H'00' ; W indicator for many instruction (not the address!)
; These are some locations used for the Bank and Indirection tests
varb0 equ H'1A' ; This is in upper part of Bank0
varb1 equ H'3A' ; This is in upper part of Bank1
varb2 equ H'5A' ; This is in upper part of Bank2
varb3 equ H'7A' ; This is in upper part of Bank3
; ************ Start Up Code **************
start:
; Set up TRIS registers
movlw H'ff'
tris porta ; PORTA is Input
clrw
tris portb ; PORTB is Output
tris portc ; PORTC is Output
movwf portb ; PORTB <= 00
; Start at begining...
goto test1
; *** TEST1 ***
; Test increment and decrement
test1:
movlw H'FD' ; W <= FD
movwf x ; X <= FD
incf x, f ; X <= FE
incf x, f ; X <= FF
incf x, f ; X <= 00
incf x, f ; X <= 01
decf x, f ; X <= 00
decf x, f ; X <= FF
decf x, f ; X <= FE
movf x, W ; W <= FE
xorlw H'FE' ; Does W == FE?
btfss STATUS, ZERO
goto fail1
goto pass1
fail1 movlw H'F1'
movwf portb ; PORTB <= F1
goto test2
pass1 movlw H'01'
movwf portb ; PORTB <= 01
goto test2
; *** TEST2 ***
; Test Add and Subtract
test2:
clrf x ; x <= 0
movlw H'A0' ; w <= A0
addwf x, f ; x <= A0
addwf x, f ; x <= 40
btfss STATUS, CARRY ; carry should be set, if not then FAIL
goto fail2
addwf x, f ; x <= E0
btfsc STATUS, CARRY ; carry should be clear, if not then FAIL
goto fail2
movf x, W ; W <= E0
xorlw H'E0' ; Does W == E0?
btfss STATUS, ZERO
goto fail2
movlw H'30'
subwf x, f ; x <= B0
subwf x, f ; x <= 80
btfss STATUS, CARRY ; borrow should be clear, if not then FAIL
goto fail2
subwf x, f ; x <= 50
subwf x, f ; x <= 20
subwf x, f ; x <= F0
btfsc STATUS, CARRY ; borrow should be set, if not then FAIL
goto fail2
movf x, W ; W <= F0
xorlw H'F0' ; Does W == F0?
btfss STATUS, ZERO
goto fail2
goto pass2
fail2 movlw H'F2'
movwf portb ; PORTB <= F2
goto test3
pass2 movlw H'02'
movwf portb ; PORTB <= 02
goto test3
; *** TEST3 ***
; Test Rotates, start with rrf
test3: bsf STATUS, CARRY ; CARRY <= 1
movlw H'A0' ; w <= A0
movwf x ; x <= A0 (10100000)
rrf x, f ; x <= D0 (11010000)
bsf STATUS, CARRY ; CARRY <= 1
rrf x, f ; x <= E8 (11101000)
bcf STATUS, CARRY ; CARRY <= 0
rrf x, f ; x <= 74 (01110100)
bcf STATUS, CARRY ; CARRY <= 0
rrf x, f ; x <= 3A (00111010)
movf x, W ; W <= 3A
xorlw H'3A' ; Does W == 3A ?
btfss STATUS, ZERO
goto fail3
; Do same sort of thing using rlf
bsf STATUS, CARRY ; CARRY <= 1
movlw H'A0' ; w <= A0
movwf x ; x <= A0 (10100000)
rlf x, f ; x <= 41 (01000001)
bsf STATUS, CARRY ; CARRY <= 1
rlf x, f ; x <= 83 (10000011)
bcf STATUS, CARRY ; CARRY <= 0
rlf x, f ; x <= 06 (00000110)
bcf STATUS, CARRY ; CARRY <= 0
rlf x, f ; x <= 0C (00001100)
movf x, W ; W <= 0C
xorlw H'0C' ; Does W == 0C ?
btfss STATUS, ZERO
goto fail3
goto pass3
fail3 movlw H'F3'
movwf portb ; PORTB <= F3
goto test4
pass3 movlw H'03'
movwf portb ; PORTB <= 03
goto test4
; *** TEST4 ***
; Test TIMER0
;
; Option is:
; 7 6 5 4 3 2 1 0
; +---------------------------------------------------------------+
; | x | x | T0CS | T0SE | PSA | PS2 | PS1 | PS0 |
; +---------------------------------------------------------------+
; T0CS - 0: Use chip clock as source, 1: use external pin
; T0SE - Invert input when using external source
; PSA - Set to '0' to use prescaler
; PS2:PS0 - Divide by 0 up to 7
;
; To use maximum prescaler using internal clock, program
; OPTION register to:
;
; 00000111 = 7
;
test4: movlw H'07' ; Set TIMER0 prescaler to ...
option
movlw H'FF' ; W <= FF
movwf x ; x <= FF
clrf TIMER0
test4loop:
decf x, f
btfss STATUS, ZERO
goto test4loop
movf TIMER0, W
xorlw H'03' ; Does W == ?
btfss STATUS, ZERO
goto fail4
goto pass4
fail4 movlw H'F4'
movwf portb ; PORTB <= F4
goto test5
pass4 movlw H'04'
movwf portb ; PORTB <= 04
goto test5
; *** TEST5 ***
; Test various logic instructions
test5: clrf x ; x <= 00
movlw B'00000101' ; W <= 00000101
iorwf x, f ; x <= 00000101
comf x, f ; x <= 11111010
movlw B'1110011' ; W <= 01110011
andwf x, f ; x <= 01110010
movlw B'11110000' ; W <= 11110000
xorwf x, f ; x <= 10000010
swapf x, f ; x <= 00101000
; Check results up to now
movfw x ; W <= 00101000
xorlw B'00101000' ; Does W == 00101000
btfss STATUS, ZERO
goto fail5
; Check some bit tests and clears. Invert
; all the bits of X which is now: 00101000
bsf x, 7
bsf x, 6
bcf x, 5
bsf x, 4
bcf x, 3
bsf x, 2
bsf x, 1
bsf x, 0 ; x <= 11010111
movfw x ; W <= 11010111
xorlw B'11010111' ; Does W == 11010111
btfss STATUS, ZERO
goto fail5
goto pass5
fail5 movlw H'F5'
movwf portb ; PORTB <= F5
goto test6
pass5 movlw H'05'
movwf portb ; PORTB <= 05
goto test6
; *** TEST6 ***
; Test subroutines
test6: clrf x ; x <= 00
call sub6c ; x <= 2
call sub6c ; x <= 4
call sub6c ; x <= 6
goto cont6
sub6a: movlw 5 ; ** ADD 5 TO X
addwf x, f
retlw 0
sub6b: movlw 3 ; ** SUB 3 FROM X
subwf x, f
retlw 0
sub6c: call sub6a ; ** ADD 2 TO X (by call others)
call sub6b ;
retlw 0
cont6: movfw x ; W <= 6
xorlw 6 ; Does W == 6
btfss STATUS, ZERO
goto fail6
goto pass6
fail6 movlw H'F6'
movwf portb ; PORTB <= F6
goto test7
pass6 movlw H'06'
movwf portb ; PORTB <= 06
goto test7
; *** TEST7 ***
; Test Register File Banks and address mapping, and Indirect Addressing.
;
; Write the values 1,2,3 and 4 into 4 different
; registers in each of the 4 banks. Then, go back and verify.
; This will test bank logic and indirect addressing.
;
test7: movlw 1 ; W <= 1
movwf x ; X <= 1
; Point to a location in upper part of Bank #0
movlw varb0 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; Point to a location in upper part of Bank #1
movlw varb1 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; Point to a location in upper part of Bank #2
movlw varb2 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; Point to a location in upper part of Bank #3
movlw varb3 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; OK. Go back and read each count and output to PORTB
movlw varb0 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'01' ; Should be 1
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
movlw varb1 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'02' ; Should be 2
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
movlw varb2 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'03' ; Should be 3
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
movlw varb3 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'04' ; Should be 4
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
goto pass7
fail7 movlw 0F7h
movwf portb ; PORTB <= F7
goto test8
pass7 movlw 007h
movwf portb ; PORTB <= 07
goto test8
; *** TEST8 ***
; Quick test of PORTA input with PORTC
;
; Expect the repeating patter of H'55' and H'AA' on PORTA.
; Those inputs should remain on input for at least a dozen cycles.
; Code waits until both values are seen. Also use TIMER0 so
; that we do not hang. Timeout if nothing is seen after, say, about
; 64 cycles.
test8: movlw H'07' ; Set TIMER0 prescaler to ...
option
clrf TIMER0
; Look for H'55' first...
test8scan1:
movf porta, W ; Read PORTA
xorlw H'55' ; Is it H'55'?
btfsc STATUS, ZERO
goto test8scan2 ; Saw the H'55'!
movf TIMER0,W ; Check the timer
xorlw H'02'
btfsc STATUS, ZERO
goto fail8 ; Timeout :-(
goto test8scan1 ; Keep scanning
; Now look for H'AA'...
test8scan2:
movf porta, W ; Read PORTA
xorlw H'AA' ; Is it H'AA'?
btfsc STATUS, ZERO
goto pass8 ; Saw the H'AA'!
movf TIMER0,W ; Check the timer
xorlw H'04'
btfsc STATUS, ZERO
goto fail8 ; Timeout :-(
goto test8scan2 ; Keep looking
fail8 movlw H'F8'
movwf portb ; PORTB <= F8
goto test9
pass8 movlw H'08'
movwf portb ; PORTB <= 08
goto test9
test9:
done: goto done ; Spin forever..
; *** End of main code, place start vector ***
org D'2047'
goto start
END
命令プログラムは次のようになります.@000 CFF
@001 005
@002 040
@003 006
@004 007
@005 026
@006 A07
@007 CFD
@008 02A
@009 2AA
@00A 2AA
@00B 2AA
@00C 2AA
@00D 0EA
@00E 0EA
@00F 0EA
@010 20A
@011 FFE
@012 743
@013 A15
@014 A18
@015 CF1
@016 026
@017 A1B
@018 C01
@019 026
@01A A1B
@01B 06A
@01C CA0
@01D 1EA
@01E 1EA
@01F 703
@020 A37
@021 1EA
@022 603
@023 A37
@024 20A
@025 FE0
@026 743
@027 A37
@028 C30
@029 0AA
@02A 0AA
@02B 703
@02C A37
@02D 0AA
@02E 0AA
@02F 0AA
@030 603
@031 A37
@032 20A
@033 FF0
@034 743
@035 A37
@036 A3A
@037 CF2
@038 026
@039 A3D
@03A C02
@03B 026
@03C A3D
@03D 503
@03E CA0
@03F 02A
@040 32A
@041 503
@042 32A
@043 403
@044 32A
@045 403
@046 32A
@047 20A
@048 F3A
@049 743
@04A A5A
@04B 503
@04C CA0
@04D 02A
@04E 36A
@04F 503
@050 36A
@051 403
@052 36A
@053 403
@054 36A
@055 20A
@056 F0C
@057 743
@058 A5A
@059 A5D
@05A CF3
@05B 026
@05C A60
@05D C03
@05E 026
@05F A60
@060 C07
@061 002
@062 CFF
@063 02A
@064 061
@065 0EA
@066 743
@067 A65
@068 201
@069 F03
@06A 743
@06B A6D
@06C A70
@06D CF4
@06E 026
@06F A73
@070 C04
@071 026
@072 A73
@073 06A
@074 C05
@075 12A
@076 26A
@077 C73
@078 16A
@079 CF0
@07A 1AA
@07B 3AA
@07C 20A
@07D F28
@07E 743
@07F A8D
@080 5EA
@081 5CA
@082 4AA
@083 58A
@084 46A
@085 54A
@086 52A
@087 50A
@088 20A
@089 FD7
@08A 743
@08B A8D
@08C A90
@08D CF5
@08E 026
@08F A93
@090 C05
@091 026
@092 A93
@093 06A
@094 99E
@095 99E
@096 99E
@097 AA1
@098 C05
@099 1EA
@09A 800
@09B C03
@09C 0AA
@09D 800
@09E 998
@09F 99B
@0A0 800
@0A1 20A
@0A2 F06
@0A3 743
@0A4 AA6
@0A5 AA9
@0A6 CF6
@0A7 026
@0A8 AAC
@0A9 C06
@0AA 026
@0AB AAC
@0AC C01
@0AD 02A
@0AE C1A
@0AF 024
@0B0 20A
@0B1 020
@0B2 2AA
@0B3 C3A
@0B4 024
@0B5 20A
@0B6 020
@0B7 2AA
@0B8 C5A
@0B9 024
@0BA 20A
@0BB 020
@0BC 2AA
@0BD C7A
@0BE 024
@0BF 20A
@0C0 020
@0C1 2AA
@0C2 C1A
@0C3 024
@0C4 200
@0C5 F01
@0C6 743
@0C7 ADB
@0C8 C3A
@0C9 024
@0CA 200
@0CB F02
@0CC 743
@0CD ADB
@0CE C5A
@0CF 024
@0D0 200
@0D1 F03
@0D2 743
@0D3 ADB
@0D4 C7A
@0D5 024
@0D6 200
@0D7 F04
@0D8 743
@0D9 ADB
@0DA ADE
@0DB CF7
@0DC 026
@0DD AE1
@0DE C07
@0DF 026
@0E0 AE1
@0E1 C07
@0E2 002
@0E3 061
@0E4 205
@0E5 F55
@0E6 643
@0E7 AED
@0E8 201
@0E9 F02
@0EA 643
@0EB AF6
@0EC AE4
@0ED 205
@0EE FAA
@0EF 643
@0F0 AF9
@0F1 201
@0F2 F04
@0F3 643
@0F4 AF6
@0F5 AED
@0F6 CF8
@0F7 026
@0F8 AFC
@0F9 C08
@0FA 026
@0FB AFC
@0FC AFC
@7FF A00
シミュレーションプログラムは次のようになります.`timescale 1ns / 10ps
module TB;
// For 50Mhz, Period is 20ns, So Set CLKHI to 10 and CLKLO to 10
parameter CLKHI = 10;
parameter CLKLO = 10;
parameter NOP = 1;
parameter MOVWF = 2;
parameter CLRW = 3;
parameter CLRF = 4;
parameter SUBWF = 5;
parameter DECF = 6;
parameter IORWF = 7;
parameter ANDWF = 8;
parameter XORWF = 9;
parameter ADDWF = 10;
parameter MOVF = 11;
parameter COMF = 12;
parameter INCF = 13;
parameter DECFSZ = 14;
parameter RRF = 15;
parameter RLF = 16;
parameter SWAPF = 17;
parameter INCFSZ = 18;
parameter BCF = 19;
parameter BSF = 20;
parameter BTFSC = 21;
parameter BTFSS = 22;
parameter OPTION = 23;
parameter SLEEP = 24;
parameter CLRWDT = 25;
parameter TRIS = 26;
parameter RETLW = 27;
parameter CALL = 28;
parameter GOTO = 29;
parameter MOVLW = 30;
parameter IORLW = 31;
parameter ANDLW = 32;
parameter XORLW = 33;
// Basic Interface to the PI CCPU
reg clk;
reg reset;
// Declare I/O Port connections
reg [7:0] porta; // INPUT
wire [7:0] portb; // OUTPUT
wire [7:0] portc; // OUTPUT
// Debug Signals
wire [11:0] debuginst;
wire [7:0] debugdbus;
wire [7:0] debugsbus;
wire [7:0] debugw;
// Declare ROM and rom signals
wire [10:0] inst_addr;
wire [11:0] inst_data;
wire [7:0] fsr = CPU.fsr;
wire [7:0] status = CPU.status;
wire [7:0] debugmem0 = CPU.regs.dram.mem[0];
wire [7:0] debugmem1 = CPU.regs.dram.mem[1];
wire [7:0] debugmem2 = CPU.regs.dram.mem[2];
wire [7:0] debugmem3 = CPU.regs.dram.mem[3];
wire [7:0] debugmem9 = CPU.regs.dram.mem[9];
wire [7:0] debugmem10 = CPU.regs.dram.mem[10];
wire [7:0] debugmem11 = CPU.regs.dram.mem[11];
wire [7:0] debugmem12 = CPU.regs.dram.mem[12];
// Instantiate one CPU to be tested.
CPU CPU(
.clk (clk),
.rst_n (reset),
.inst_addr(inst_addr),
.inst_data(inst_data),
.portain (porta),
.portbout (portb),
.portcout (portc),
.debuginst(debuginst),
.debugdbus(debugdbus),
.debugsbus(debugsbus),
.debugw (debugw)
);
// Instantiate the Program RAM.
pram pram (
.clk (clk),
.address (inst_addr),
.we (1'b0), // This testbench doesn't allow writing to PRAM
.din (12'b000000000000), // This testbench doesn't allow writing to PRAM
.dout (inst_data)
);
initial begin
$display ("Free-RISC8. Version 1.0");
basic;
end
event ENDSIM;
// Capture some data
task capture_data;
begin
$dumpfile ("risc8.vcd");
$dumpvars (0, TB);
@(ENDSIM);
$dumpflush;
end
endtask
// Reset
task reset_pic;
begin
reset = 0;
#200;
reset = 1;
$display ("End RESET.");
end
endtask
// Drive the clock input
task drive_clock;
begin
clk = 0;
forever begin
#(CLKLO) clk = 1;
#(CLKHI) clk = 0;
end
end
endtask
// ************* BASIC CONFIDENCE Test Tasks **************
//
// BASIC CONFIDENCE Test.
//
// This task will fork off all the other necessary tasks to cause reset, drive the clock, etc. etc.
//
//
task basic;
integer num_outputs;
integer num_matches;
integer num_mismatches;
begin
$display ("Free-RISC8 1.0. This is the BASIC CONFIDENCE TEST.");
#1;
$display ("Loading program memory with %s", "basic.rom");
$readmemh ("C:/Users/HOWARD/Desktop/Microchip_16C57/basic.rom", pram.mem);
fork
// Capture data
capture_data;
// Run the clock
drive_clock;
// Do a reset
reset_pic;
// Monitor the number of cycles and set an absolute maximum number of cycles.
monitor_cycles (5000);
// More specific monitors
//monitor_inst;
monitor_portb;
monitor_portc;
// Drive PORTA with a toggling pattern. This is for one of the subtests.
//
basic_drive_porta;
// Monitor the counting pattern on PORTB. This is our self-checking scheme for the test.
//
begin
//
num_outputs = 9; // Expect exactly 7 changes on the PORTB (0..6).
// Call the following task which will watch PORTB for the patterns.
//
basic_monitor_output_signature (num_outputs, num_matches, num_mismatches);
// See how we did!
repeat (2) @(posedge clk);
$display ("Done monitoring for output signature. %0d Matches, %0d Mismatches.", num_matches, num_mismatches);
if (num_matches == num_outputs && num_mismatches == 0) begin
$display ("SUCCESS.");
end
else begin
$display ("Test FAILED!!");
end
// We are done. Throw the ENDSIM event.
->ENDSIM;
#0;
$finish;
end
// Catch end of simulation event due to max number of cycles or pattern from PIC code.
begin
@(ENDSIM); // Catch the event.
// Got it!
$display ("End of simulation signalled. Killing simulation in a moment.");
#0; // Let anything else see this event...
$finish;
end
join
end
endtask
// Monitor PORTB for an incrementing pattern. This is how we are doing our self-checking.
// A good run will count from ZERO up to some number.
//
task basic_monitor_output_signature;
input num_outputs;
output num_matches;
output num_mismatches;
integer num_outputs;
integer num_matches;
integer num_mismatches;
integer i;
reg [7:0] expected_output;
begin
num_matches = 0;
num_mismatches = 0;
expected_output = 8'h00;
i = 0;
while (i < num_outputs) begin
// Wait for any change on output port B.
@(portb);
#1; // Wait for a moment for any wiggling on different
// bits to seetle out, just in case there's any gate-level going on..
if (portb == expected_output) begin
$display ("MONITOR_OUTPUT_SIGNATURE: Expected output observed on PORTB: %h", portb);
num_matches = num_matches + 1;
end
else begin
$display ("MONITOR_OUTPUT_SIGNATURE: Unexpected output on PORTB: %h", portb);
num_mismatches = num_mismatches + 1;
end
expected_output = expected_output + 1;
i = i + 1;
end
end
endtask
task basic_drive_porta;
begin
forever begin
porta = 8'h55;
repeat (32) @(posedge clk);
porta = 8'hAA;
repeat (32) @(posedge clk);
end
end
endtask
// ***************************** Generic Tasks ************************** //
// CYCLE monitor and end-of-simulation checker.
//
task monitor_cycles;
input max_cycles;
integer max_cycles;
integer cycles;
begin
cycles = 0;
fork
// Count cycles.
forever begin
@(posedge clk);
cycles = cycles + 1;
end
// Watch for max cycles. If we detect max cycles then throw our testbench ENDSIM event.
//
begin
wait (cycles == max_cycles);
$display ("MAXIMUM CYCLES EXCEEDED!");
->ENDSIM;
end
join
end
endtask
// Generic Debug Display stuff.
//
task monitor_rom;
begin
forever begin
@(negedge clk);
$display ("ROM Address = %h, Data = %h", inst_addr, inst_data);
end
end
endtask
task monitor_porta;
reg [7:0] last_porta;
begin
forever begin
@(negedge clk);
if (last_porta !== porta) begin
$display ("porta changes to: %h", porta);
last_porta = porta;
end
end
end
endtask
task monitor_portb;
reg [7:0] last_portb;
begin
forever begin
@(negedge clk);
if (last_portb !== portb) begin
$display ("MONITOR_PORTB: Port B changes to: %h", portb);
last_portb = portb;
end
end
end
endtask
task monitor_portc;
reg [7:0] last_portc;
begin
forever begin
@(negedge clk);
if (last_portc !== portc) begin
$display ("MONITOR_PORTC: Port C changes to: %h", portc);
last_portc = portc;
end
end
end
endtask
endmodule
このRISC CPUのシミュレーションにより、CPUの具体的な実装の流れをより明確に知ることができる.全体の工事については、前のブログを参考にして、私のリソースで無料でダウンロードします.
INDF equ H'00' ; Magic register that uses INDIRECT register
TIMER0 equ H'01' ; Timer register
STATUS equ H'03' ; STATUS register F3
FSR equ H'04' ; INDIRECT Pointer Register
porta equ H'05' ; I/O register F5
portb equ H'06' ; I/O register F6
portc equ H'07' ; I/O register F7
x equ H'0A' ; Our general variable
y equ H'0B' ; Another variable..
CARRY equ H'00' ; Carry bit in STATUS register
ZERO equ H'02' ; Zero bit in STATUS register
W equ H'00' ; W indicator for many instruction (not the address!)
; These are some locations used for the Bank and Indirection tests
varb0 equ H'1A' ; This is in upper part of Bank0
varb1 equ H'3A' ; This is in upper part of Bank1
varb2 equ H'5A' ; This is in upper part of Bank2
varb3 equ H'7A' ; This is in upper part of Bank3
; ************ Start Up Code **************
start:
; Set up TRIS registers
movlw H'ff'
tris porta ; PORTA is Input
clrw
tris portb ; PORTB is Output
tris portc ; PORTC is Output
movwf portb ; PORTB <= 00
; Start at begining...
goto test1
; *** TEST1 ***
; Test increment and decrement
test1:
movlw H'FD' ; W <= FD
movwf x ; X <= FD
incf x, f ; X <= FE
incf x, f ; X <= FF
incf x, f ; X <= 00
incf x, f ; X <= 01
decf x, f ; X <= 00
decf x, f ; X <= FF
decf x, f ; X <= FE
movf x, W ; W <= FE
xorlw H'FE' ; Does W == FE?
btfss STATUS, ZERO
goto fail1
goto pass1
fail1 movlw H'F1'
movwf portb ; PORTB <= F1
goto test2
pass1 movlw H'01'
movwf portb ; PORTB <= 01
goto test2
; *** TEST2 ***
; Test Add and Subtract
test2:
clrf x ; x <= 0
movlw H'A0' ; w <= A0
addwf x, f ; x <= A0
addwf x, f ; x <= 40
btfss STATUS, CARRY ; carry should be set, if not then FAIL
goto fail2
addwf x, f ; x <= E0
btfsc STATUS, CARRY ; carry should be clear, if not then FAIL
goto fail2
movf x, W ; W <= E0
xorlw H'E0' ; Does W == E0?
btfss STATUS, ZERO
goto fail2
movlw H'30'
subwf x, f ; x <= B0
subwf x, f ; x <= 80
btfss STATUS, CARRY ; borrow should be clear, if not then FAIL
goto fail2
subwf x, f ; x <= 50
subwf x, f ; x <= 20
subwf x, f ; x <= F0
btfsc STATUS, CARRY ; borrow should be set, if not then FAIL
goto fail2
movf x, W ; W <= F0
xorlw H'F0' ; Does W == F0?
btfss STATUS, ZERO
goto fail2
goto pass2
fail2 movlw H'F2'
movwf portb ; PORTB <= F2
goto test3
pass2 movlw H'02'
movwf portb ; PORTB <= 02
goto test3
; *** TEST3 ***
; Test Rotates, start with rrf
test3: bsf STATUS, CARRY ; CARRY <= 1
movlw H'A0' ; w <= A0
movwf x ; x <= A0 (10100000)
rrf x, f ; x <= D0 (11010000)
bsf STATUS, CARRY ; CARRY <= 1
rrf x, f ; x <= E8 (11101000)
bcf STATUS, CARRY ; CARRY <= 0
rrf x, f ; x <= 74 (01110100)
bcf STATUS, CARRY ; CARRY <= 0
rrf x, f ; x <= 3A (00111010)
movf x, W ; W <= 3A
xorlw H'3A' ; Does W == 3A ?
btfss STATUS, ZERO
goto fail3
; Do same sort of thing using rlf
bsf STATUS, CARRY ; CARRY <= 1
movlw H'A0' ; w <= A0
movwf x ; x <= A0 (10100000)
rlf x, f ; x <= 41 (01000001)
bsf STATUS, CARRY ; CARRY <= 1
rlf x, f ; x <= 83 (10000011)
bcf STATUS, CARRY ; CARRY <= 0
rlf x, f ; x <= 06 (00000110)
bcf STATUS, CARRY ; CARRY <= 0
rlf x, f ; x <= 0C (00001100)
movf x, W ; W <= 0C
xorlw H'0C' ; Does W == 0C ?
btfss STATUS, ZERO
goto fail3
goto pass3
fail3 movlw H'F3'
movwf portb ; PORTB <= F3
goto test4
pass3 movlw H'03'
movwf portb ; PORTB <= 03
goto test4
; *** TEST4 ***
; Test TIMER0
;
; Option is:
; 7 6 5 4 3 2 1 0
; +---------------------------------------------------------------+
; | x | x | T0CS | T0SE | PSA | PS2 | PS1 | PS0 |
; +---------------------------------------------------------------+
; T0CS - 0: Use chip clock as source, 1: use external pin
; T0SE - Invert input when using external source
; PSA - Set to '0' to use prescaler
; PS2:PS0 - Divide by 0 up to 7
;
; To use maximum prescaler using internal clock, program
; OPTION register to:
;
; 00000111 = 7
;
test4: movlw H'07' ; Set TIMER0 prescaler to ...
option
movlw H'FF' ; W <= FF
movwf x ; x <= FF
clrf TIMER0
test4loop:
decf x, f
btfss STATUS, ZERO
goto test4loop
movf TIMER0, W
xorlw H'03' ; Does W == ?
btfss STATUS, ZERO
goto fail4
goto pass4
fail4 movlw H'F4'
movwf portb ; PORTB <= F4
goto test5
pass4 movlw H'04'
movwf portb ; PORTB <= 04
goto test5
; *** TEST5 ***
; Test various logic instructions
test5: clrf x ; x <= 00
movlw B'00000101' ; W <= 00000101
iorwf x, f ; x <= 00000101
comf x, f ; x <= 11111010
movlw B'1110011' ; W <= 01110011
andwf x, f ; x <= 01110010
movlw B'11110000' ; W <= 11110000
xorwf x, f ; x <= 10000010
swapf x, f ; x <= 00101000
; Check results up to now
movfw x ; W <= 00101000
xorlw B'00101000' ; Does W == 00101000
btfss STATUS, ZERO
goto fail5
; Check some bit tests and clears. Invert
; all the bits of X which is now: 00101000
bsf x, 7
bsf x, 6
bcf x, 5
bsf x, 4
bcf x, 3
bsf x, 2
bsf x, 1
bsf x, 0 ; x <= 11010111
movfw x ; W <= 11010111
xorlw B'11010111' ; Does W == 11010111
btfss STATUS, ZERO
goto fail5
goto pass5
fail5 movlw H'F5'
movwf portb ; PORTB <= F5
goto test6
pass5 movlw H'05'
movwf portb ; PORTB <= 05
goto test6
; *** TEST6 ***
; Test subroutines
test6: clrf x ; x <= 00
call sub6c ; x <= 2
call sub6c ; x <= 4
call sub6c ; x <= 6
goto cont6
sub6a: movlw 5 ; ** ADD 5 TO X
addwf x, f
retlw 0
sub6b: movlw 3 ; ** SUB 3 FROM X
subwf x, f
retlw 0
sub6c: call sub6a ; ** ADD 2 TO X (by call others)
call sub6b ;
retlw 0
cont6: movfw x ; W <= 6
xorlw 6 ; Does W == 6
btfss STATUS, ZERO
goto fail6
goto pass6
fail6 movlw H'F6'
movwf portb ; PORTB <= F6
goto test7
pass6 movlw H'06'
movwf portb ; PORTB <= 06
goto test7
; *** TEST7 ***
; Test Register File Banks and address mapping, and Indirect Addressing.
;
; Write the values 1,2,3 and 4 into 4 different
; registers in each of the 4 banks. Then, go back and verify.
; This will test bank logic and indirect addressing.
;
test7: movlw 1 ; W <= 1
movwf x ; X <= 1
; Point to a location in upper part of Bank #0
movlw varb0 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; Point to a location in upper part of Bank #1
movlw varb1 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; Point to a location in upper part of Bank #2
movlw varb2 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; Point to a location in upper part of Bank #3
movlw varb3 ; Get Address
movwf FSR ; Set index register
movf x,W ; Get the 'x' value
movwf INDF ; Write using index
incf x,f ; Increment the counter
; OK. Go back and read each count and output to PORTB
movlw varb0 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'01' ; Should be 1
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
movlw varb1 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'02' ; Should be 2
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
movlw varb2 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'03' ; Should be 3
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
movlw varb3 ; Get Address
movwf FSR ; Set index register
movf INDF,W ; Retrieve the 'x' value to W
xorlw H'04' ; Should be 4
btfss STATUS, ZERO ; IF yes, keep going
goto fail7
goto pass7
fail7 movlw 0F7h
movwf portb ; PORTB <= F7
goto test8
pass7 movlw 007h
movwf portb ; PORTB <= 07
goto test8
; *** TEST8 ***
; Quick test of PORTA input with PORTC
;
; Expect the repeating patter of H'55' and H'AA' on PORTA.
; Those inputs should remain on input for at least a dozen cycles.
; Code waits until both values are seen. Also use TIMER0 so
; that we do not hang. Timeout if nothing is seen after, say, about
; 64 cycles.
test8: movlw H'07' ; Set TIMER0 prescaler to ...
option
clrf TIMER0
; Look for H'55' first...
test8scan1:
movf porta, W ; Read PORTA
xorlw H'55' ; Is it H'55'?
btfsc STATUS, ZERO
goto test8scan2 ; Saw the H'55'!
movf TIMER0,W ; Check the timer
xorlw H'02'
btfsc STATUS, ZERO
goto fail8 ; Timeout :-(
goto test8scan1 ; Keep scanning
; Now look for H'AA'...
test8scan2:
movf porta, W ; Read PORTA
xorlw H'AA' ; Is it H'AA'?
btfsc STATUS, ZERO
goto pass8 ; Saw the H'AA'!
movf TIMER0,W ; Check the timer
xorlw H'04'
btfsc STATUS, ZERO
goto fail8 ; Timeout :-(
goto test8scan2 ; Keep looking
fail8 movlw H'F8'
movwf portb ; PORTB <= F8
goto test9
pass8 movlw H'08'
movwf portb ; PORTB <= 08
goto test9
test9:
done: goto done ; Spin forever..
; *** End of main code, place start vector ***
org D'2047'
goto start
END
@000 CFF
@001 005
@002 040
@003 006
@004 007
@005 026
@006 A07
@007 CFD
@008 02A
@009 2AA
@00A 2AA
@00B 2AA
@00C 2AA
@00D 0EA
@00E 0EA
@00F 0EA
@010 20A
@011 FFE
@012 743
@013 A15
@014 A18
@015 CF1
@016 026
@017 A1B
@018 C01
@019 026
@01A A1B
@01B 06A
@01C CA0
@01D 1EA
@01E 1EA
@01F 703
@020 A37
@021 1EA
@022 603
@023 A37
@024 20A
@025 FE0
@026 743
@027 A37
@028 C30
@029 0AA
@02A 0AA
@02B 703
@02C A37
@02D 0AA
@02E 0AA
@02F 0AA
@030 603
@031 A37
@032 20A
@033 FF0
@034 743
@035 A37
@036 A3A
@037 CF2
@038 026
@039 A3D
@03A C02
@03B 026
@03C A3D
@03D 503
@03E CA0
@03F 02A
@040 32A
@041 503
@042 32A
@043 403
@044 32A
@045 403
@046 32A
@047 20A
@048 F3A
@049 743
@04A A5A
@04B 503
@04C CA0
@04D 02A
@04E 36A
@04F 503
@050 36A
@051 403
@052 36A
@053 403
@054 36A
@055 20A
@056 F0C
@057 743
@058 A5A
@059 A5D
@05A CF3
@05B 026
@05C A60
@05D C03
@05E 026
@05F A60
@060 C07
@061 002
@062 CFF
@063 02A
@064 061
@065 0EA
@066 743
@067 A65
@068 201
@069 F03
@06A 743
@06B A6D
@06C A70
@06D CF4
@06E 026
@06F A73
@070 C04
@071 026
@072 A73
@073 06A
@074 C05
@075 12A
@076 26A
@077 C73
@078 16A
@079 CF0
@07A 1AA
@07B 3AA
@07C 20A
@07D F28
@07E 743
@07F A8D
@080 5EA
@081 5CA
@082 4AA
@083 58A
@084 46A
@085 54A
@086 52A
@087 50A
@088 20A
@089 FD7
@08A 743
@08B A8D
@08C A90
@08D CF5
@08E 026
@08F A93
@090 C05
@091 026
@092 A93
@093 06A
@094 99E
@095 99E
@096 99E
@097 AA1
@098 C05
@099 1EA
@09A 800
@09B C03
@09C 0AA
@09D 800
@09E 998
@09F 99B
@0A0 800
@0A1 20A
@0A2 F06
@0A3 743
@0A4 AA6
@0A5 AA9
@0A6 CF6
@0A7 026
@0A8 AAC
@0A9 C06
@0AA 026
@0AB AAC
@0AC C01
@0AD 02A
@0AE C1A
@0AF 024
@0B0 20A
@0B1 020
@0B2 2AA
@0B3 C3A
@0B4 024
@0B5 20A
@0B6 020
@0B7 2AA
@0B8 C5A
@0B9 024
@0BA 20A
@0BB 020
@0BC 2AA
@0BD C7A
@0BE 024
@0BF 20A
@0C0 020
@0C1 2AA
@0C2 C1A
@0C3 024
@0C4 200
@0C5 F01
@0C6 743
@0C7 ADB
@0C8 C3A
@0C9 024
@0CA 200
@0CB F02
@0CC 743
@0CD ADB
@0CE C5A
@0CF 024
@0D0 200
@0D1 F03
@0D2 743
@0D3 ADB
@0D4 C7A
@0D5 024
@0D6 200
@0D7 F04
@0D8 743
@0D9 ADB
@0DA ADE
@0DB CF7
@0DC 026
@0DD AE1
@0DE C07
@0DF 026
@0E0 AE1
@0E1 C07
@0E2 002
@0E3 061
@0E4 205
@0E5 F55
@0E6 643
@0E7 AED
@0E8 201
@0E9 F02
@0EA 643
@0EB AF6
@0EC AE4
@0ED 205
@0EE FAA
@0EF 643
@0F0 AF9
@0F1 201
@0F2 F04
@0F3 643
@0F4 AF6
@0F5 AED
@0F6 CF8
@0F7 026
@0F8 AFC
@0F9 C08
@0FA 026
@0FB AFC
@0FC AFC
@7FF A00
`timescale 1ns / 10ps
module TB;
// For 50Mhz, Period is 20ns, So Set CLKHI to 10 and CLKLO to 10
parameter CLKHI = 10;
parameter CLKLO = 10;
parameter NOP = 1;
parameter MOVWF = 2;
parameter CLRW = 3;
parameter CLRF = 4;
parameter SUBWF = 5;
parameter DECF = 6;
parameter IORWF = 7;
parameter ANDWF = 8;
parameter XORWF = 9;
parameter ADDWF = 10;
parameter MOVF = 11;
parameter COMF = 12;
parameter INCF = 13;
parameter DECFSZ = 14;
parameter RRF = 15;
parameter RLF = 16;
parameter SWAPF = 17;
parameter INCFSZ = 18;
parameter BCF = 19;
parameter BSF = 20;
parameter BTFSC = 21;
parameter BTFSS = 22;
parameter OPTION = 23;
parameter SLEEP = 24;
parameter CLRWDT = 25;
parameter TRIS = 26;
parameter RETLW = 27;
parameter CALL = 28;
parameter GOTO = 29;
parameter MOVLW = 30;
parameter IORLW = 31;
parameter ANDLW = 32;
parameter XORLW = 33;
// Basic Interface to the PI CCPU
reg clk;
reg reset;
// Declare I/O Port connections
reg [7:0] porta; // INPUT
wire [7:0] portb; // OUTPUT
wire [7:0] portc; // OUTPUT
// Debug Signals
wire [11:0] debuginst;
wire [7:0] debugdbus;
wire [7:0] debugsbus;
wire [7:0] debugw;
// Declare ROM and rom signals
wire [10:0] inst_addr;
wire [11:0] inst_data;
wire [7:0] fsr = CPU.fsr;
wire [7:0] status = CPU.status;
wire [7:0] debugmem0 = CPU.regs.dram.mem[0];
wire [7:0] debugmem1 = CPU.regs.dram.mem[1];
wire [7:0] debugmem2 = CPU.regs.dram.mem[2];
wire [7:0] debugmem3 = CPU.regs.dram.mem[3];
wire [7:0] debugmem9 = CPU.regs.dram.mem[9];
wire [7:0] debugmem10 = CPU.regs.dram.mem[10];
wire [7:0] debugmem11 = CPU.regs.dram.mem[11];
wire [7:0] debugmem12 = CPU.regs.dram.mem[12];
// Instantiate one CPU to be tested.
CPU CPU(
.clk (clk),
.rst_n (reset),
.inst_addr(inst_addr),
.inst_data(inst_data),
.portain (porta),
.portbout (portb),
.portcout (portc),
.debuginst(debuginst),
.debugdbus(debugdbus),
.debugsbus(debugsbus),
.debugw (debugw)
);
// Instantiate the Program RAM.
pram pram (
.clk (clk),
.address (inst_addr),
.we (1'b0), // This testbench doesn't allow writing to PRAM
.din (12'b000000000000), // This testbench doesn't allow writing to PRAM
.dout (inst_data)
);
initial begin
$display ("Free-RISC8. Version 1.0");
basic;
end
event ENDSIM;
// Capture some data
task capture_data;
begin
$dumpfile ("risc8.vcd");
$dumpvars (0, TB);
@(ENDSIM);
$dumpflush;
end
endtask
// Reset
task reset_pic;
begin
reset = 0;
#200;
reset = 1;
$display ("End RESET.");
end
endtask
// Drive the clock input
task drive_clock;
begin
clk = 0;
forever begin
#(CLKLO) clk = 1;
#(CLKHI) clk = 0;
end
end
endtask
// ************* BASIC CONFIDENCE Test Tasks **************
//
// BASIC CONFIDENCE Test.
//
// This task will fork off all the other necessary tasks to cause reset, drive the clock, etc. etc.
//
//
task basic;
integer num_outputs;
integer num_matches;
integer num_mismatches;
begin
$display ("Free-RISC8 1.0. This is the BASIC CONFIDENCE TEST.");
#1;
$display ("Loading program memory with %s", "basic.rom");
$readmemh ("C:/Users/HOWARD/Desktop/Microchip_16C57/basic.rom", pram.mem);
fork
// Capture data
capture_data;
// Run the clock
drive_clock;
// Do a reset
reset_pic;
// Monitor the number of cycles and set an absolute maximum number of cycles.
monitor_cycles (5000);
// More specific monitors
//monitor_inst;
monitor_portb;
monitor_portc;
// Drive PORTA with a toggling pattern. This is for one of the subtests.
//
basic_drive_porta;
// Monitor the counting pattern on PORTB. This is our self-checking scheme for the test.
//
begin
//
num_outputs = 9; // Expect exactly 7 changes on the PORTB (0..6).
// Call the following task which will watch PORTB for the patterns.
//
basic_monitor_output_signature (num_outputs, num_matches, num_mismatches);
// See how we did!
repeat (2) @(posedge clk);
$display ("Done monitoring for output signature. %0d Matches, %0d Mismatches.", num_matches, num_mismatches);
if (num_matches == num_outputs && num_mismatches == 0) begin
$display ("SUCCESS.");
end
else begin
$display ("Test FAILED!!");
end
// We are done. Throw the ENDSIM event.
->ENDSIM;
#0;
$finish;
end
// Catch end of simulation event due to max number of cycles or pattern from PIC code.
begin
@(ENDSIM); // Catch the event.
// Got it!
$display ("End of simulation signalled. Killing simulation in a moment.");
#0; // Let anything else see this event...
$finish;
end
join
end
endtask
// Monitor PORTB for an incrementing pattern. This is how we are doing our self-checking.
// A good run will count from ZERO up to some number.
//
task basic_monitor_output_signature;
input num_outputs;
output num_matches;
output num_mismatches;
integer num_outputs;
integer num_matches;
integer num_mismatches;
integer i;
reg [7:0] expected_output;
begin
num_matches = 0;
num_mismatches = 0;
expected_output = 8'h00;
i = 0;
while (i < num_outputs) begin
// Wait for any change on output port B.
@(portb);
#1; // Wait for a moment for any wiggling on different
// bits to seetle out, just in case there's any gate-level going on..
if (portb == expected_output) begin
$display ("MONITOR_OUTPUT_SIGNATURE: Expected output observed on PORTB: %h", portb);
num_matches = num_matches + 1;
end
else begin
$display ("MONITOR_OUTPUT_SIGNATURE: Unexpected output on PORTB: %h", portb);
num_mismatches = num_mismatches + 1;
end
expected_output = expected_output + 1;
i = i + 1;
end
end
endtask
task basic_drive_porta;
begin
forever begin
porta = 8'h55;
repeat (32) @(posedge clk);
porta = 8'hAA;
repeat (32) @(posedge clk);
end
end
endtask
// ***************************** Generic Tasks ************************** //
// CYCLE monitor and end-of-simulation checker.
//
task monitor_cycles;
input max_cycles;
integer max_cycles;
integer cycles;
begin
cycles = 0;
fork
// Count cycles.
forever begin
@(posedge clk);
cycles = cycles + 1;
end
// Watch for max cycles. If we detect max cycles then throw our testbench ENDSIM event.
//
begin
wait (cycles == max_cycles);
$display ("MAXIMUM CYCLES EXCEEDED!");
->ENDSIM;
end
join
end
endtask
// Generic Debug Display stuff.
//
task monitor_rom;
begin
forever begin
@(negedge clk);
$display ("ROM Address = %h, Data = %h", inst_addr, inst_data);
end
end
endtask
task monitor_porta;
reg [7:0] last_porta;
begin
forever begin
@(negedge clk);
if (last_porta !== porta) begin
$display ("porta changes to: %h", porta);
last_porta = porta;
end
end
end
endtask
task monitor_portb;
reg [7:0] last_portb;
begin
forever begin
@(negedge clk);
if (last_portb !== portb) begin
$display ("MONITOR_PORTB: Port B changes to: %h", portb);
last_portb = portb;
end
end
end
endtask
task monitor_portc;
reg [7:0] last_portc;
begin
forever begin
@(negedge clk);
if (last_portc !== portc) begin
$display ("MONITOR_PORTC: Port C changes to: %h", portc);
last_portc = portc;
end
end
end
endtask
endmodule