BX portable game console | project collaboration


If you are going to use my i2c master code, note that the interface to it is rather inconsistent. I have hacked about at it to get various devices working and it now needs some tidying up, and some of the unused features could be removed to save resources, I did not write the original - it came from a Xilinx forum as an answer to someone’s question.


I managed to find some nunchucks today so I can start poking around with them. I don’t have the little connectors for them, so I’ll splice some connectors into the cables and see if I can get something going.

If you are going to use my i2c master code, note that the interface to it is rather inconsistent. I have hacked about at it to get various devices working and it now needs some tidying up, and some of the unused features could be removed to save resources, I did not write the original - it came from a Xilinx forum as an answer to someone’s question.

No problems. I just had a bit of a look at the code then. I must admit - I was quite surprised at how complex the I²C bus protocol is. :woozy_face: If we use two controllers, do we need to instantiate two instances of the bus master?

In other news, I whipped up a little tile based demo to see how the graphics & audio work together. Here’s a small little video of the current state of things:

What’s working:

  • 320x240 tilemap VGA output
  • X&Y offset registers to position the screen viewport in tile space
  • 4-channel audio - each channel has waveform select, pulsewidth, frequency and volume registers.
  • timer/IRQ based code for audio routine
  • a very simple audio playback routine that’s polled via a timer IRQ - just enough to get what you hear in the video going really.

What’s not going yet:

  • sprites (I’m still not sure how best to handle these efficiently with multiple of them on the screen at the same time. Ideally I’d like to support 8 sprites at, say, 16x16 pixels with transparency, and without an explosion in logic to deal with them. A cuppa and some pondering might be required).
  • Scale/direction registers for stepping through the tile map (to enable mode-7-like graphics)
  • I²C nunchucks. I’m scared of these now. :sweat_smile:

Also, I agree with you about the header-based code and project layout. I kind of went down that path originally because I was using apio, and struggling to get it to figure out what my top-level module was (it turns out there’s a workaround for apio too). For this project I’m predominantly using make now, so I’m happy to restructure the layout to make it a bit more “standard”.


I just tried the version you checked in and for me it hangs in set_vid_x_ofs.


I don’t think the Nunchuk lets you changes its i2c address, so yes we would need two bus masters.

I am currently struggling, trying to get Pacman working on my version. I am still using 640x480 VGA. I will look at your VGA code.


I just tried the version you checked in and for me it hangs in set_vid_x_ofs.

Noooooooo! Boooooo! :space_invader:

You’ve definitely built and uploaded both the firmware and the hardware?

That’s a really weird place for it to hang. I wonder if the IRQ handler kicked in and crashed? It’s been working pretty solidly for me here.

Out of interest, how do you know it was at that point? (I’m wondering if you have some great debugging tools I’m not aware of!!!) :slight_smile:



Ignore that. It looks like a managed to use your firmware and my bitstream. So I did not upload your bin file. Just trying to understand your make files.


I was getting output on the uart saying “Setting up IRQs …” and then hanging, so I put in a few more print statements.


so I put in a few more print statements.

I’m simultaneously happy and sad to hear that :slight_smile:


Sorry that I had no magic diagnostics. How do you upload your hardware.bin file? I don’t see it in any of the make files. Do you do it manually after doing as make in the hdl directory?


For now, yes, tinyprog -p hardware.bin

I’ll add that to the Makefile.


Thanks. You song example is working for me now.

How did you fix the interrupt register saving problem. If I selectively take some of your stuff, I will probably need that fix.


It was a pretty simple fix in picosoc.v - the q registers are located above the normal register bank, so I extended the address lines in the register file to be 6 bits rather than 5 (and increased the number of registers too).

In theory, you could get by without doing this - for example, using a pre-allocated RAM location as temporary swap space, and modifying the IRQ handler in start.S to suit. I preferred the idea of using the Q regs. It felt cleaner to me. YMMV.

I’ve actually made a few other changes too - for example - I’ve relocated the stack to memory at 0x00000000 - 0x00000400 - so that if it overflows it doesn’t get into the data/heap space, and so I don’t have to keep changing it’s location if I increase memory size. There were corresponding changes to the IRQ handler and initialisation code to deal with this. I’m also no longer using .bss.


Do your changes fix the data initialisation problem that @mattvenn reported?


Actually, yes, I suppose they do. I just looked at Luke’s repo and it doesn’t look like he’s doing the data copy in his startup code. Clifford’s code (from the main picorv32 repo) does, but it’s got a whole bunch of other stuff in there that’s not so useful (eg. running tests etc).

The last few days have been a bit of a blur for me. I think my init code (and segment definition file) is a combination of Luke’s, Clifford’s, and some custom bits. It works for me. It may or may not work for others :slight_smile:

ETA: I don’t think my startup.S file will work unmodified with the TinyFPGA-BX picosoc because of the stack layout I’m using. For one, I’m saving the registers to memory location 0x0000 (ie. the last available location in the stack space) in the interrupt handler (and allocating a bit of space above that for the stack for the interrupt handler C code), and also because I’m also forcing the stack pointer to 0x0400 just before jumping to main.

I think it’s probably kind of hard to come up with a “one-size-fits-all” block of startup code due to the configurability of PicoSoC, but it’s pretty straightforward to mix-and-match what’s needed… especially once you’ve done the seemingly mandatory day or so of headscratching to figure it all out :wink:



It is heroic of you to mess with all the ld script and assembler stuff.


Heroic? Nah. Silly maybe. Naive and persistent perhaps.

I started down the path of assembly and ld scripts when I was trying to get timer interrupts to work. I wanted to use interrupts for driving the audio playback routine and also for future use in a game event loop timer.

That’s how I came to discover how the Q regs worked (or didn’t as the case may be in the TinyFPGA example). It was also when I realised that the default IRQ handler code in Luke’s start.S seemed to be writing registers to a location that I thought should contain other data (eg. the stack). And writing stuff there might cause problems. So I started looking into that. And then realised that the initialised data wasn’t being copied across to RAM. And then another few distractions later I found that I’d modified quite a lot of stuff. And also probably learnt quite a lot about how things hang together. :man_shrugging:

If Luke’s interested I can submit a few issues to the TinyFPGA-BX repo and send through some pull requests to sort them out. I submitted an issue to Clifford’s picorv32 project about the qregs and register file, but Clifford rightly pointed out that the defaults in his copy of picosoc.v (QREGS disabled) mean that it would never be a problem, and that adding the extra registers consumed extra resources which he considered a bad default.

Anyway, long story short, I have no idea what I’m doing, and therefore I blindly stumble into areas where others may fear to tread. :smiley:


If we are running VGA at 320x240, it can be done with a half-speed clock. 2 x 16Mhz is close enough to 31.5Mhz, so it should be possibly to avoid the PLL and have a single 16Mhz clock domain.


Good thinking. Using a slower clock could definitely work. Although, with the dual-port RAM I don’t think it’s as much of an issue as I imagined it might be.

I had a slow day today, but I did manage to get 8 sprites working.

Each sprite has a control register that can be used to set:

  • x/y position
  • image index to use for the sprite (sprites are 1bpp, and there’s enough BRAM to store 64 different images)
  • colour
  • whether or not the sprite is visible
  • the sprite memory has been IO-mapped into the CPU’s address space, and, like the texture and tile memory, can be written to but not read.

Sprites are priority based, so sprite 0 will be drawn on top of sprite 1, which will be drawn on top of sprite 2 etc.

Things are starting to get a little tight again. Hopefully I can still fit the i2c controller in.

Speaking of I2C - I’m also thinking it might be possible to hook up two nunchucks, and use the same clk signal for both. Hopefully without running two separate I2C master blocks. We might even be able to do some trickery like sending the same “read-data” request to both nunchucks at the same time, and then just stream both results back.



The single clock could save a few PLBs, so might be worth trying. I have had a single sprite working for a while but your implementation is much more complete. I now have it in my version, but I have not merged everything yet. I have 8 (mainly very simple) examples in my version so updating it and testing them all takes a while.

I improved the Nunchuk interface and put it in separate .h and .c files. I have moved common C code to a libraries directory - a bit like Arduino librtaries.

I will improve the interface to i2c a bit. Your idea of driving two Nunchuks in parallel sounds interesting. I am using a Nunchuk to control my Pacman game but there is a lot of work to do on it.


I get a lot of flickering and no sprites. I am running the latest version with no changes other than a couple of pin assignments for audio and the uart. This is your version, not my merged version.