Closed elialm closed 1 year ago
I planned initially to make something with JTAG. Maybe I could access the boot ROM via JTAG. This seems to be impossible according to the documentation.
Looking at the diagram above, it shows no connection between JTAG and the user logic. Maybe I could use I2C or SPI to talk to user logic. However, this means I'd have to get some bridge between my PC and the board. Something like an USB-to-I2C device. Drivers for this probably exist, meaning I could use the i2c-tools
in Linux to do stuff with it.
I've looked into the datasheet to see what is possible. It is possible to use the SPI or I2C peripheral in slave mode to wait for data, which can then be accessed inside the FPGA fabric. I've made a block diagram of a possible idea.
I could make some sort of debug component, which can be used to address the wishbone bus. This would allow me to access the entire memory map of the cartridge. I could also access the MBCH directly. However, I have a feeling that will be more difficult to implement. More investigation into this will be required.
As there will be 2 bus masters with this solution, it would be wise to inhibit the GB decoder and keep the Gameboy in reset. There could be some danger in letting the Gameboy run while the program is changed (eg. accidental writes to the SD card, possibly corrupting the filesystem). I could have an external pin I keep asserted which activates the debug core and takes control of the wishbone bus.
I also need the aforementioned bridge. The cheapest and most flexible way for me would be to use an Arduino. I can treat it as a serial device, which interprets commands from a script on my PC and does "stuff" with the debug core.
I've chosen to use I2C to communicate with the FPGA. Since SPI is already being used by the FPGA to master communication with the SD card, using it also as a slave is going to be difficult since it would need to be able to change its configuration while online. This will probably require an extra FSM to r/w over WishBone, which can be quite large to implement. Since I am not using and don't plan to use I2C, I'll just set it as a slave and use that.
Holy crap, I just got a great idea! Instead of developing an entire debug core to master the WishBone bus, I could just implement a debug program to run on the boot ROM. The program could just look for a boot condition (eg. SELECT pressed) and boot into some sort of debug mode when that condition is satisfied.
The functionality to R/W from/to memory is then activated by the boot software. The boot software could then listen for incoming commands and react accordingly. E.g. it can receive a write command and then write the next few bytes to some address. I could also finally implement a DMA to hopefully speed up this process. It will be slow compared to a firmware method due to the speed limitations of the GB bus, but it'll be much easier to implement.
This should also eliminate the need of extra firmware, giving me more freedom when adding other (more important) components. I'd rather have a software solution, since adding extra memory is easier than using a bigger FPGA. The extra I2C controller won't be needed as well, since I can easily configure the SPI controller to be a slave via the firmware.
I'll look further into this possibility and see what I need to add to implement it.
I made this design based on the software approach.
The way it should work is like so:
This is all assuming the program is on the bridge. I plan to create some kind of command based protocol between the bridge and my PC. This would give me great flexibility in how I use it. I will probably write a script that uses this protocol to ultimately write the program to the cart.
There were some issues with the hard routed SPI CSS pin on the breakout board. Apparently this pin is also connected to some optional SPI Flash under the breakout board (see images below).
I don't have access to this pin via the pin headers on the board, so I'll just route a different pin to be the SPI CSS.
After exploring the software approach, I've determined it to be unsatisfactory. Having the bootloader handle code uploads had some unforseen problems. The main issue was timing constraints with the software controlled pins. Since the program pins were controlled by software, I miss pin changes. The GPIO controller is implemented on the cartridge, so I cannot set interrupts on the Gameboy to check for pin changes. This meant that I could miss some bytes coming in while uploading. To solve this issue, I would need to decrease the SPI speed drastically, which would not only be annoyingly slow, but also guarantee nothing. With program uploads, I want to be sure that the program is uploaded correctly. I don't want the uploaded program to have wierd bugs in it due to some transmission error.
I am therefore moving to the firmware based approach. I think I can get away with the following:
The programming sequence will look something like so:
I've been able to get SPI communication working with the simple SPI programmer I made for the Arduino Pro Mini. I can now send bytes of data to the FPGA, which can be read from the WishBone bus. For testing, I've used the memory testing program on the Gameboy to test the registers.
To test the SPI functionality, I just write a byte from the computer and read it on the Gameboy. I then send a byte from the Gameboy and read it from my laptop. I used hterm for serial communication with the SPI programmer. I also included an exurb from the SPI status register definition to read the status bits from the test.
Here was the test procedure (reading the Gameboy screen memory transaction history from bottom-to-top):
0x00
to 0xA056
. This is done to put the cartridge's SPI controller in slave mode. This is normally not necessary since it is slave by default, but the memory testing program puts it master mode due to me using it previously to communicate with the SD card.0x10
from 0xA05A
. To illustrate that the RRDY
bit is 0
. After this read, I send a command to the SPI programmer to write 0x69
to the SPI bus.0x1C
from 0xA05A
. To illustrate that the RRDY
bit is now 1
.0x69
from 0xA05B
. This shows that the byte sent from the SPI programmer was read successfully.0x14
from 0xA05A
. This shows that the RRDY
bit is now cleared after a read. Also notice the TRDY
bit is 1
.0xE1
to 0xA059
. This will write this value to the SPI master during the next SPI transaction.0x00
from 0xA05A
. Notice that bit RRDY
and TRDY
are both 0
. After this, I send the byte 0x00
from the SPI programmer.0x1C
from 0xA05A
. Afterwards, the RRDY
and TRDY
are both 1
again. On the hterm window, I can also observe that the value 0xE1
was received successfully by the programmer.0x00
from 0xA05B
. This was the byte sent by the programmer.
Registers used on the test are:
0xA056
: SPI control register 20xA059
: SPI data transmit register0xA05A
: SPI status register0xA05B
: SPI data receive registerHaving the SPI experiment success, the end now seems nearer. There are some problems yet needing fixing, but progress is coming along nicely.
Now that SPI works, I can focus on the debug core mentioned in https://github.com/elialm/ecgc-firmware/issues/2#issuecomment-1251012782. This core will take the data sent over the SPI bus to write to the boot ROM following the ideas presented in https://github.com/elialm/ecgc-firmware/issues/2#issuecomment-1370960490.
There are some things to keep in mind:
TRDY
and RRDY
of the SPISR
register to outside pins.
Continuing on this issue, I plan to first design the debug core state machine. With this, I know what is needed from the DMA core and I can proceed to design that. With both of them designed, I'll finally develop them for testing and deploying.
sigh... I changed the design again... but it's a very important change.
I've decided to forgo using the EFB SPI controller in favor of a softend solution. This will provide the following benefits:
I also thought about making the debug core more flexible. There will be a point in time where I need to write software to be ran from DRAM. I would also benefit me to be able to write code from my laptop directly to the DRAM. Therefore, I decided to make the core so that I can tell it which address it needs to write to. This way, I can write to any arbitrary address (including DRAM). I'll probably make some kind of command protocol in firmware. I was avoiding this since the beginning, but it seems like it really is the way to go. I'll use it as an experiment to see how big such a command decoding component can be.
I think I completely implemented the debug core. I now just need to make a few adjustments to the rest of the cartridge to be able to add it:
DBG_ENABLE
pin.0x00
, since that is a NOP instruction. But it is left to the developer to NOT run from cartridge code when the DMA is active.HAHA, IT WORKS!
I've implemented the debug core and integrated it with the rest of the cartridge. I can now send commands via the ecgc-spi-programmer and master the entire bus from a serial monitor on my PC. I've also changed the boot_rom component to be RAM, so I can write to it whenever the cart is in debug more.
I'll document the changes at a later date (probably tomorrow). I also still need to write the upload script, but I'll make that into another repository.
Anyhow, this issue is now closed for this repo, since everything is implemented on the FPGA side.
It takes ages for me to upload changes to the boot ROM. The boot ROM is programmed alongside the FPGA fabric, so changing a line in the code means recompiling and uploading the entire FPGA. This takes multiple clicks and ~2 min of waiting, which adds up after 50 code changes.
A method needs to be implemented to make changes to boot ROM on the fly. It would also be nice to access the program memory this way as well. I could use it to test the memory or to upload larger programs.