Foreword
In the previous post I gave a quick rundown on what I plan for the next couple of posts, which is to get an Amiga core running on a Zybo board.
In this post we will get the fx68k core running on an FPGA, which is an FPGA implementation for the 68000 CPU.
Ironically, the FPGA I will be using for this exercise will not be the Zybo board, but one of my lower spec FPGA boards, the Basys 3, which is also Xilinx based.
The reason for this decision is because in the Zybo board one needs to wrap every core into an IP, which is a bit time consuming, if you are after quick R&D. Once we got to a point where many of the components are running, we can move to the Zybo board.
Simulation issues
As with many FPGA projects, one always start testing your design with simulation.
In the couple of years I was playing with FPGA's, I found that the fx68k was one of the cores I wasn't able to run in a simulator. I am referring here to the simulators that is free of charge. Not sure about commercial ones.
When trying to simulate fx86 within Vivado, it will be stuck in the Elaboration step for hours. Even after 7 hours, Vivado couldn't get passed this step.
When trying to use iVerilog, it didn't understand all the SystemVerilog syntax in the design.
Verilator's support for SystemVerilog is quite good, but in the fx68k design there are some structures, where part of the bits is defined as wires and the rest of the bits is defined as registers. Verilator doesn't like such structures.
So, concerning testing the fx68k core, I will run it on the FPGA itself. This proofed not to be a major issue.
If anyone had any luck in simulating fx68k with a particular simulator, please let me know.
Setting up the project
As mentioned earlier, I will not be using my Zybo board to test the fx68k, but a Basys 3 board, in which you don't need to package all your cores into IP blocks.
We start by creating a top module block:
module top( input clk, ); ... endmodule
When working with the Basys 3 board, it is important to constraint all the ports of your top level block in an XDC file.
In this case we have an input clock which we constrain as follows:
set_property PACKAGE_PIN W5 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk]
module top( input btnC, input clk, output [15:0] led ); ... wire clk_16mhz; ... clk_wiz_0 clk_wiz_0 ( // Clock out ports .clk_out1(clk_16mhz), // Clock in ports .clk_in1(clk) ) endmodule
clk_16mhz is the clock signal clocking at 16MHz, and we will use this clock in our design.
Next, let us see how to connect the ports of the fx68k core:
fx68k fx68k( .clk(clk_16mhz), .HALTn(1), // Used for single step only. Force high if not used // These two signals don't need to be registered. They are not async reset. .extReset(button_reset), // External sync reset on emulated system .pwrUp(button_reset), // Asserted together with reset on emulated system coldstart .enPhi1(phi), .enPhi2(~phi), // Clock enables. Next cycle is PHI1 or PHI2 .eRWn(read_write), .ASn(As), .LDSn(Lds), .UDSn(Uds), .DTACKn(0), .VPAn(1), .BERRn(1), .BRn(1), .BGACKn(1), .IPL0n(1), .IPL1n(1), .IPL2n(1), .iEdb(data_in), .oEdb(data_out), .eab(add) );
There are a couple of ports that we can leave unconnected for now. Let us quickly go through the purpose of some of these ports:
- extReset and pwrUp: In my design I have linked these ports to a button on the Basys board. Preferably one should pass the button through a debounce core, so you don't have erratic spikes on the reset port.
- eWRn: This port indicates to memory whether the fx68k want to read or write. Read is indicated with a 1 and a write with a 0.
- ASn, LDSn, UDSn: These signals are asserted during read/write cycles. We don't really need this for our fx68k, but it helps to see it on a signal trace to see if everything is working correctly.
- iEdb and oEdb: This is for data in and data out from memory.
- eab: Address request to memory
always @(negedge clk_16mhz) begin phi <= ~phi; end
module uRom( input clk, input [UADDR_WIDTH-1:0] microAddr, output logic [UROM_WIDTH-1:0] microOutput); reg [UROM_WIDTH-1:0] uRam[ UROM_DEPTH]; initial begin $readmemb("microrom.mem", uRam); end always_ff @( posedge clk) microOutput <= uRam[ microAddr]; endmodule module nanoRom( input clk, input [NADDR_WIDTH-1:0] nanoAddr, output logic [NANO_WIDTH-1:0] nanoOutput); reg [NANO_WIDTH-1:0] nRam[ NANO_DEPTH]; initial begin $readmemb("nanorom.mem", nRam); end always_ff @( posedge clk) nanoOutput <= nRam[ nanoAddr]; endmodule
Hello World boot
- movew #1285,%d0: Here we load the register d0 with the value 1285
- movew %d0, 0x00858585: Next, we store the value we loaded in the register d0 to memory at location address 0x858585.
- jsr 0x93e86: Jump to subroutine at memory location 0x93e86.
When RESET and HALT are driven by an external device, the entire system, including theprocessor, is reset. Resetting the processor initializes the internal state. The processorreads the reset vector table entry (address $00000) and loads the contents into thesupervisor stack pointer (SSP). Next, the processor loads the contents of address $00004(vector table entry 1) into the program counter.
always @(posedge clk_16mhz) begin if (add == 16'h0) begin data_in <= 7; end else if (add == 16'h1) begin data_in <= 16'h9fe7; end else if (add == 16'h2) begin data_in <= 4; end else if (add == 16'h3) begin data_in <= 16'hfe00; end else if (add == 20'h27f00) begin data_in <= 16'h303c; //load immediate end else if (add == 20'h27f01) begin data_in <= 16'h0505; end else if (add == 20'h27f02) begin data_in <= 16'h33c0; //store end else if (add == 20'h27f03) begin data_in <= 16'h0085; end else if (add == 20'h27f04) begin data_in <= 16'h8585; end else if(add == 20'h27f05) begin data_in <= 16'h4eb9; end else if (add == 20'h27f06) begin data_in <= 9; end else if (add == 20'h27f07) begin data_in <= 16'h3e86; end else begin data_in <= 16'h33c0; end end
Test Results
An address error exception occurs when the processor attempts to access a word or longword operand or an instruction at an odd address.
In Summary
In this post managed to get the fx68k core to run on Basys 3 development board.
In the Test result section, I spend a bit of time unpacking the resulting waveform.
In the next post we will continue our journey to get an Amiga core to run on a Zybo/Basys 3 board.
Till next time!