// // SYNTHETIC PIC 3.0 5/1/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 // picdram.v - Memory model for the DATA memory (e.g. Register File) // picpram.v - Memory model for the PROGRAM memory. // convert.pl - Perl script used to translate MPLAB's "Disassembled Code" output // into the Verilog $readmemh compatible file // test*.asm - (note the wildcard..) Several test programs used // to help debug the verilog. I used MPLAB and the simulator // to develop these programs and get the expected results. // Then, I ran them on Verilog-XL where they appeared to // match. // // Copyright, Tom Coonan, '97. // Use freely, but not for resale as is. You may use this in your // own projects as desired. Just don't try to sell it as is! // // `timescale 1ns / 10ps // For 50Mhz, period is 20ns, so set CLKHI to 10 and CLKLO to 10 `define CLKHI 10 `define CLKLO 10 module pictest; // Select which test to run HERE.. (use ZERO to run all tests) parameter TEST_NUMBER = 0; parameter CAPTURE = 0; // Set to 1 to enable, 0 to disable parameter COMPARE = 0; // Set to 1 to enable, 0 to disable // *** 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 cycles; // Cycles counter. // For debug.. reg [7:0] last_porta; reg [7:0] last_portb; reg [7:0] last_portc; // Vectors integer dbg_capture; integer dbg_compare; // Test Vector stuff. reg [7:0] results_vector; reg [7:0] expected_vector[0:10000]; integer resultsfp; integer expectedfp; integer vector_number; integer nummiscompares; integer numgoodcompares; // *** Basic Interface to the PICCPU reg clk; reg reset; reg enable; // Declare I/O Port connections reg [7:0] porta; wire [7:0] portb; wire [7:0] portc; // Declare ROM and rom signals wire [10:0] pramaddr; wire [11:0] pramdata; // *** Expansion Interface wire [7:0] expdin; wire [7:0] expdout; wire [6:0] expaddr; wire expread; wire expwrite; // *** Debug Interface reg [7:0] last_debugw; reg [10:0] last_debugpc; reg [11:0] last_debuginst; reg [7:0] last_debugstatus; wire [7:0] debugw; wire [10:0] debugpc; wire [11:0] debuginst; wire [7:0] debugstatus; // Phase outputs wire q1,q2,q3,q4; // Instantiate one PICCPU to be tested. piccpu piccpu_inst ( .clk (clk), .reset (reset), .enable (enable), .paddr (pramaddr), .pdata (pramdata), .portain (porta), .portbout (portb), .portcout (portc), .expdin (expdin), .expdout (expdout), .expaddr (expaddr), .expread (expread), .expwrite (expwrite), .debugw (debugw), .debugpc (debugpc), .debuginst (debuginst), .debugstatus (debugstatus), .q1(q1), .q2(q2), .q3(q3), .q4(q4) ); // Instantiate the Program RAM picpram picpram ( .clk (clk), .address (pramaddr), .we (1'b0), // This testbench doesn't allow writing to PRAM .din (12'b000000000000), // This testbench doesn't allow writing to PRAM .dout (pramdata) ); // Instantiate one PICEXP (Expansion) module. picexp1 picexp1_inst ( .clk (clk), .reset (reset), .expdin (expdin), .expdout (expdout), .expaddr (expaddr), .expread (expread), .expwrite (expwrite) ); // Capture some data initial begin $dumpfile ("pic.vcd"); $dumpvars (0, pictest); end // Reset task reset_pic; begin enable = 1; reset = 1; #200; reset = 0; $display ("End RESET."); end endtask // Drive the clock input initial begin clk = 0; forever begin #(`CLKLO) clk = 1; #(`CLKHI) 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_capture = CAPTURE; dbg_compare = COMPARE; end // Call the appropriate test task based on the TEST_NUMBER parameter set at top. // initial begin if (TEST_NUMBER == 0) begin // This means, run them all! test1; test2; test3; test4; test5; test6; test7; test8; test9; test10; $display ("Done."); $finish; end else begin case (TEST_NUMBER) 1: begin test1; $finish; end 2: begin test2; $finish; end 3: begin test3; $finish; end 4: begin test4; $finish; end 5: begin test5; $finish; end 6: begin test6; $finish; end 7: begin test7; $finish; end 8: begin test8; $finish; end 9: begin test9; $finish; end 10: begin test10; $finish; end default: begin $display ("ERROR: Unknown Test Number: %0d", TEST_NUMBER); $finish; end endcase end $display ("Done."); $finish; end // ************* Specific Tests ************** // // Each test corresponds to one test program running on the PIC. Each // test task is responsible for reading in the correct .ROM file, providing // any necessary stimulus, specify any vector file names and selecting what // debug display flags are desired. Also, it calls the one 'run_sim' task // which actually runs the simulation; resets the pic, monitors the number // of cycles, and does any test vector stuff. // task test1; begin $display ("SYNTHETIC PIC 2.0. This is TEST #1"); #1; // Only Watch Port B dbg_showportb = 1; dbg_showcycles = 1; dbg_showinst = 0; $readmemh ("test1.rom", picpram.mem); if (dbg_compare) $readmemh ("test1.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test1.exp"); run_sim (2000); 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", picpram.mem); if (dbg_compare) $readmemh ("test2.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test2.exp"); run_sim (2000); 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", picpram.mem); if (dbg_compare) $readmemh ("test3.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test3.exp"); run_sim (2000); 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", picpram.mem); if (dbg_compare) $readmemh ("test4.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test4.exp"); run_sim (2000); 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", picpram.mem); if (dbg_compare) $readmemh ("test5.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test5.exp"); run_sim (2000); end endtask task test6; begin $display ("SYNTHETIC PIC 2.0. This is TEST #6"); #1; // Watch Port B and C dbg_showporta = 1; dbg_showportb = 1; dbg_showportc = 1; $readmemh ("test6.rom", picpram.mem); if (dbg_compare) $readmemh ("test6.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test6.exp"); fork repeat (10) begin porta = $random; #8000; end run_sim (2000); join 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", picpram.mem); if (dbg_compare) $readmemh ("test7.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test7.exp"); run_sim (2000); 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", picpram.mem); if (dbg_compare) $readmemh ("test8.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test8.exp"); run_sim (2000); end endtask task test9; begin $display ("SYNTHETIC PIC 2.0. This is TEST #9"); #1; // Watch All ports dbg_showportb = 1; dbg_showportc = 1; $readmemh ("test9.rom", picpram.mem); if (dbg_compare) $readmemh ("test9.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test9.exp"); run_sim (2000); end endtask task test10; begin $display ("SYNTHETIC PIC 2.0. This is TEST #10"); #1; // Only Watch Port B dbg_showportb = 1; $readmemh ("test10.rom", picpram.mem); if (dbg_compare) $readmemh ("test10.exp", expected_vector); if (dbg_capture) resultsfp = $fopen ("test10.exp"); run_sim (2000); end endtask // ******** END Of TEST TASKS // ***************** Run Simulation task ******************* // task run_sim; input max_cycles; integer max_cycles; begin $display ("running sim for %d cycles", max_cycles); reset_pic; cycles = 0; // Set these in case we are doing any vector processing vector_number = 0; nummiscompares = 0; numgoodcompares = 0; // Run until max_cycles is exceeded. // while (cycles < max_cycles) begin // Wait for each clock edge @(posedge clk); cycles = cycles + 1; if ((cycles % 256) == 0) begin if (dbg_showcycles) begin $display ("#Cycles = %0d", cycles); end end // Wait for the strobe time in case we are doing any vector processing #(`CLKHI + `CLKLO - (`CLKHI+`CLKLO)/50); results_vector = portb; // This is our result vector // Are we capturing vectors? if (dbg_capture) begin // Write the vector to the 'expected' file. $fdisplay (resultsfp, "%x", results_vector); vector_number = vector_number + 1; end // Are we comparing results with expected? if (dbg_compare) begin // Do they match? if (results_vector != expected_vector[vector_number]) begin // NO! We have a miscompare. $display ("Miscompare at vector #%0d at time %t. Expected = %x, Actual = %x", vector_number, $time, expected_vector[vector_number], results_vector); nummiscompares = nummiscompares + 1; end else begin // Count these as a sanity check. numgoodcompares = numgoodcompares + 1; end vector_number = vector_number + 1; end end // Must be done. $display ("Maximum cycles (%0d) Exceeded. Halting simulation.", max_cycles); if (dbg_compare) begin $display (" # of Good compares = %0d, # of Miscompares = %0d", numgoodcompares, nummiscompares); end end endtask // ************** Debug Display stuff ************************* initial begin forever begin @(negedge clk); if (dbg_showrom) begin if (q4) begin $display ("ROM Address = %h, Data = %h", pramaddr, pramdata); end end end end initial begin forever begin @(negedge clk); if (dbg_showporta) begin if (last_porta !== porta) begin $display ("porta changes to: %h", porta); last_porta = porta; end end end end initial begin forever begin @(negedge clk); if (dbg_showportb) begin if (q1) begin if (last_portb !== portb) begin $display ("portb changes to: %h", portb); last_portb = portb; end end end end end initial begin forever begin @(negedge clk); if (dbg_showportc) begin if (q1) begin if (last_portc !== portc) begin $display ("portc changes to: %h", portc); last_portc = portc; end end end end end initial begin forever begin @(negedge clk); if (dbg_showw) begin if (q1) begin if (debugw !== last_debugw) begin $display ("W = %0h", debugw); end last_debugw = debugw; end end end end initial begin forever begin @(negedge clk); if (dbg_showpc) begin if (q1) $display ("PC = %0h", debugpc); end end end reg [11:0] last_pc; always @(posedge clk) begin last_pc = debugpc; end initial begin #6; forever begin @(negedge clk); if (dbg_showinst) begin if (q1) begin 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 end endmodule