Spinning a prototype based on the iCEBreaker schematic

I’ve been prototyping a design (a synthesizer module) on the icebreaker board for the last month or so and i think its time to get it off the breadboard.

My plan was (for a first spin) use the same ftdi chip, fpga, and flash scheme that is used on the icebreaker with the hopes that I can keep programming it in the same way (currently using fpgawars/apio).

I use diptrace so I would be re-laying out the board using the icebreaker schematic as a reference as opposed to actually expanding the icebreaker schematic. Also adding several ADCs, DACs, and other application specific circuitry.

Is there anything I should watch out for or think about before getting into detailed design/layout?

Is there a straightforward way to program the flash using the open source toolchain without the FTDI chip?

Hi,

If you want to eliminate the ftdi chip you’re best to consider a design based on fomu (https://github.com/im-tomu/fomu-hardware). It uses the valentyusb hdl library to bit bang usb and manage the flash using dfu.

Cheers,
Tom

Hi, yep. I hadn’t seen the fomu but I also have luke valenty’s tinyfpga bx and had seperately come to the conclusion his bootloader was probably the way to go.

He has a version of the bootloader with pin definitions for the icebreaker on the github:

Where I am at at the moment is still similar to the icebreaker but pushing the ftdi chip offboard to be used only for initial programming then plan on updating the firmware with the TinyFPGA bootloader.

The tinyfpga-bootloader code in that repo (1) is for the icebreaker-bitsy, which is an unreleased board. (2) It doesn’t work …

I would encourage you to look at https://github.com/smunaut/ice40-playground/tree/master/projects/riscv_usb which contains a working USB implementation that works on the icebreaker and supports DFU firmware upgrade. And I’d be happy to help you adapt it to your custom board.

Hi tnt, thanks for replying

I saw that code was for an unreleased board but the pin definitions matched the icebreaker and I figured that would be enough, did not realise it did not work!

I was looking at that riscv_usb implementation. I wasn’t quite sure what to look at there. Is that implementing a full CPU and then implementing USB within that? Or is the USB bootloader seperate from the CPU?

My goal here is just to be able to update firmware over USB, if I were to use this method what would that look like from the firmware upload side on the PC? Apio still usable?

tinyfpga prog was originally written for another ice40 family that has much faster fabric than the UP5k used in the icebreaker and as such it actually doesn’t meet timings, among the other issues it has … At some point I did manage to get it recognized with some hacking, but questionable stability.

The code in riscv_usb is similar to what you would find in a classic microcontroller with USB. There is a dedicated hardware USB block that handles the low layers of USB (so it’s not bit-banged or anything like that, it’s a true hardware USB to meet all the critical timings of USB), but the higher level of the USB stack are running on a riscv CPU. (eventually I’d like to get it running on a smaller cpu than a risc-v, it’s WiP). tinyprog is “all hardware” but it also doesn’t pass the USB compliance tests at all, while here I was aiming for full compliance with the spec for maximum compatibility.

However the way it’s designed to work is to have a separate “boot loader” bitstream in flash that’s triggered for instance when a given button is pressed at bootup. So the way I did it so far is to have the default bitstream be a very simple one that just looks at the button state to decide what bitstream to boot ( https://github.com/smunaut/ice40-playground/tree/usb/projects/boot_stub ). Then depending on that it will either boot the “bootloader” bitstream, or the “application” bitstream. Then in the application bitstream itself, if it’s a “usb-based bitstream”, include DFU descriptors there to allow reboot from application to bootloader, or if the application bitstream doesn’t use USB at all, just include a small helper that reboots into bootloader mode when a button is pressed for a long time for instance ( https://github.com/smunaut/ice40-playground/blob/usb/projects/riscv_usb/rtl/dfu_helper.v ).

On the PC side the way it would look (assuming your application bitstream has no USB support at all), well when you plug the board, by default it doesn’t do anything. But if you power it while pressing the button, then it boot in DFU mode and shows up as a normal DFU device to your PC where you can use dfu-util to upload a bitstream. Or you can also press that button for X seconds and it would also reboot in that mode. So to upload a new bitstream you’d need to do either of those to get it to show up on the PC, and then use dfu-util to load the new bitstream and reboot in application mode.

I don’t use APIO but it looks like it has DFU support and so you’d probably just need to add an entry for your board in the boards.json file and then upload from APIO should work. Althouh you’ll need to manually get the board to DFU mode first. (unless your application bitstream has USB support itself and can accept the command to reboot to DFU programatically).

Does that make sense ?

That does make sense.

Attached is my current schematic (power is on another page). It’s mostly based off of the icebreaker schematic with the ftdi chip left off. Current plan is to spin another tiny board with just the ftdi chip and supporting circuitry for initial programming (or tap the lines from my icebreaker) then try your bootloader for successive firmware updates.

The pins chosen for USB etc are the same as what luke valenty used, looking at the pcf file in your git it looks like while may I may need to switch a couple pins they are all gpio and all there. Am I obviously missing anything that your boot loader would need?

I don’t see any problem with the including the boot loader helper as part of my application. The dfu configuration actually looks friendlier than my current programming configuration. Getting full USB compliance going sounds like quite a feat!

The intent of this board isn’t really to copy the icebreaker or tinyfpga, more to quickly prove to myself that this platform is viable for (open source) production purposes (even though I have already sunk months into the verilog design) and to get the project away from the breadboard/dev board stage. I haven’t picked a license yet but the current plan is to post up full source code and schematics once things are at a mostly-working stage.

I’m planning to lay this board out this week as a somewhat roomy 4 layer board, keep it big, easy, and loaded with test points, before shrinking things down with the adc, dac, etc (currently on separate boards) included. The verilog I have been prototyping on the icebreaker is for eurorack and/or standalone audio synthesizer purposes, so it does not need to communicate via usb at all. The usb is strictly for firmware update purposes.

Thanks for your help!

Huh picture is a bit low resolution to really check anything.

Things to pay attention to:

  • All 3 USB pins on the same IO bank (so all IOB_xxx or all IOT_xxx). 1.5k resistor for pull up line to D+ and then 47R resistors as series termination for the USB data lines.
  • 12 MHz clock input on same pin as the icebreaker (36 IIRC)
  • Make sure the other pin on the same IO tile is only used for output and not input (since that line will be unusable for input due to PLL constrains using that IO tile input path)
  • Flash must have enough space for the equivalent of 4 bitstreams.
  • Ideally flash should also support the “get uid” command (that’s used to generate a serial number automatically)
  • Need a ‘user’ button to be able to enter boot loader mode
  • Ideally a RGB led is used to report the current state (like it blinks fast in DFU mode), but that’s optional.

Got it!
Clock input is on pin 35 (same as icebreaker), how would I find out what the other pin on the same IO tile is?

Is “get uid” same as “Read Unique ID”? I currently have the same/similar flash as the one on the icebreaker on there, W25Q128JVSIM.

The other pin is pin 37. (and I was wrong, it’s not the same IO tile, it’s the adjacent IO tile since the PLL spans 2 tiles).

Yeah, Read Unique ID. The flash from the icebreaker is fine.

Hi all,

Sorry for resurrecting an old thread, but I thought better to reuse the previous conversation that has a lot of valuable info already.

I have started playing with the icebreaker board just recently, and like @eschlappi I also want to build a cheap minimal board I can leave in my projects. Naturally, the question of bootloader comes. @eschlappi did it work out fine for you? @esden are you still planning to release the icebreaker bitsy board anytime soon?

I did build up my board and have been prototyping with it but haven’t gotten to the bootloader yet. I took one of my icebreakers, depopulated the ram, and used the ram connections to program my board. Probably not the cheapest/most efficent ftdi programmer available but it works fine. Not really a field deployable solution though.

I’ll push the bootloader up the to do list and try out tnt’s solution.
Just a note, since posting in this thread I have moved away from APIO to a makefile calling yosys, nextpnr and then using iceprog to load the bitstream, just like in all the icebreaker examples.

@tnt So I finally went ahead and downloaded the boot-stub project. I altered the top-icebreaker.pcf so the USB pins matched my board (I’m using 43, 38, 42 for usdb_dp, usb_pu, usb_dn, all other pins are the same) then built the project using the makefile and loaded it on my board with iceprog (using the ftdi chip from a butchered icebreaker as mentioned above).

I then tried plugging in the usb and using dfu-util --list, while holding (and then not holding) the user button. Nothing was shown in the list. I don’t have any experience with dfu-util or debugging USB. Any suggestions for troubleshooting or did I miss a step?

I haven’t tried adding the dfu-helper to my project yet. I figured getting the board to be recognized by dfu-util was the first thing to try.

@eschlappi I think you have to build the whole projects/riscv_usb shebang and use that as a separate image together with boot-stub. You can make one image out of two using icemulti command. Yesterday I have tried a basic warmboot configuration - one image was a simple bootloader, another a blinky example. Here is my “bootloader” code, being first (number 0) image it starts by default and uses warmboot to boot into the image number 1 upon button press.

module top(
		input wire CLK,
		output wire LED1,
		output wire LED5,
		output wire LEDR_N,
		input wire BTN_N
	);

	reg flag = 0;
	assign LEDR_N = 0;
	assign LED5 = (1 == flag);

	always @(posedge CLK) begin
		if(0 == BTN_N) begin
			flag <= 1;
		end else begin
			flag <= 0;
		end
	end

	SB_WARMBOOT wb(
		.S0(1),
		.S1(0),
		.BOOT(flag)
	);

endmodule

I am compiling riscv gcc right now to test riscv-usb solution.

GCC finally has compiled and I have build the project. It works! Firmware on riscv cpu is talking via serial and a new usb device gets enumerated upon command via serial.

Unfortunately, this is just a basic USB interface, DFU functionality is for us to write I guess :wink:

Oh! Duh, that makes more sense than what I was doing. I’m trying to follow in your footsteps now.

So I tried to compile the riscv-usb project and got and error:
make[1]: riscv-none-embed-gcc: Command not found

I did build this toolchain: https://github.com/riscv/riscv-gnu-toolchain
(though I’m not convinced it’s working right)

Am I missing anything obvious?

I bet it’s compiled under the different name - for example riscv64-unknown-elf-gcc.

I have spent several hours compiling it then I have realized, I already have it - I have downloaded “fomu toolchain” during FPGA workshop in CCC -
https://github.com/icebreaker-fpga/icebreaker-workshop fomu has a binary release with all the stuff -
https://github.com/im-tomu/fomu-toolchain

You now either can find out to what name riscv-gcc has compiled or just download fomu toolchain, in both cases change to proper name in the riscv_usb Makefile.

I didn’t do much on this yet, but I have looked into fomu project - they have a DFU bootloader implemented in software, I bet something can be gleaned form there. In the meantime I got fascinated with the whole picorv32 thing, so having a small distraction while implementing ws2812b peripheral for the MCU, just got picorv32 interrupts working yesterday.

Okay, yeah. I changed the line in the makefile in /riscv_usb/fw

from: CROSS ?= riscv-none-embed-
to: CROSS ?= riscv64-unknown-elf-

and now it compiles. I can call up the serial console and it connects to ttyUSB1 but I do not get a command prompt. I’m guessing this is when I need to use icemulti to package together the riscv_usb and boot_stub (or your bootloader code for testing?)?

I switched back to the icebreaker (as a known good) to do all this, btw. Obviously I’m flying pretty blind here, any recommendation on where to start with trying to understand the riscv/picorv32 side of this? go deeper on the FOMU I guess? Sounds like you are making progress!

DFU support is present. Make sure you’re in the ‘usb’ branch of the repo which has the latest usb code.

Also, I’m not here very often but if you checkout the discord chat there is much better chance to find me there.

Doh! Thank you @tnt!

@eschlappi did you program both the fpga bitstream and the firmware?

I got the usb branch working today. I think you have to build two bitstream versions -
make BOOT_CODE=dfu
cp build-tmp/riscv_usb.bin riscv-usb-dfu.bin
make BOOT_CODE=app
cp build-tmp/riscv_usb.bin riscv-usb-app.bin

Yes, you have to merge boot_stub and two images:
icemulti -o bootset.bin -a 18 -p0 -v ../boot_stub/build-tmp/boot_stub.bin riscv-usb-dfu.bin riscv-usb-app.bin

-a 18 will put the application bitstream at 0x80000, dfu driver has the same address for the bitstream bank update

make -C fw prog_dfu will load dfu firmware

Now you can select dfu bitstream via button and finally we can run dfu-util to load the app processor firmware:

sudo dfu-util -R -S e46894b0a7375b2b -a 1 -D fw/fw_app.bin
note the -a 1 - thats the dfu “bank”, there are two setup in usb_dfu.c. -a 0 will load the fpga bitstream into warmboot slot 2 - 2’b10 at 0x80000 in flash.

One more slot it still free and boot_stub lets to select it, but you will have to put a dummy bitstream there via icemulti and add another slot to usb_dfu.c

Congrats @tnt for really clean code!