Monday, October 9, 2023

Gatemate SoM

Today I got from our production the first samples of the world-wide first GateMate SoM TEG2000.

For initial testing, I grabbed a TEB0707 4x5 SoM carrier board.

Inserting TEG2000 into TEB0707 motherboard. Checking power supply requirements, aha 5V! Need to adjust the lab supply that was previously set to 12V.

Power on! No smoke, so far so good. One RED Led on TEG2000 is on. 

Starting ToolZ, clicking detect! Cool the JTAG ID is reported successfully. The module is not dead.

Do the GateMate programming tools also work? For this, we need to use Zadig first. Replacing the driver for the first instance (channel A) of the FT2232. Executing:

run.bat jtag

the RED led goes off, it seems that it is inverse DONE. So we have the GateMate FPGA configured successfully. The default LED blinky is not blinking as I did not adjust the PIN constraints yet.

Pressing F4 in FAR Commander and looking at the constraints, comparing with the TEG2000 schematic. Aha, the CLK pin happens to be the same, but the LED location is different, adjusting it. What about the reset pin? TEB0707 has a user button that I could use for soft reset, but for LED blinky we do not really need a real reset, a dummy is equally OK. TEG2000 happens to have one I/O pin connected to GND and one I/O pin connected to VCC, so it is ideal to be used as a fake reset. Adjusting the pin-map file for this. 

running GateMate tools again. And we have an LED blinking on TEG2000! Amazing!

TEG2000 has 8 I/Os connected through auto direction sense level shifter to fixed function pins of the 4x5 standard. As example FT2232 channel B UART pins are connected to those I/Os. So we could also test UART function. Adding the rxd and txd ports to the blink.vhd file and in the body of the VHDL file:

    txd <= rxd;

Done! This if all goes well should implement UART echo so we can verify FT2232 channel B. OK, we need to adjust the pin constraints also for the UART. Done! Running the tools and configuring the FPGA. Starting Putty terminal, and indeed we have an echo! So the pin mapping is correct and the level shifter works as well. Great!

What about starting from SPI flash?

run.bat jtag-flash

Can it be that fast? Clicking master reset button on TEB0707. And the LED blinks again, did it really load from SPI flash? To be sure I turn the power off and on again, and indeed the FPGA is loaded and the user LED blinks.

Now all we need is some documentation! But I can list the main features here:

  • Format: Trenz Electronic 4x5 (40 x 50 mm)
  • B2B Connectors: 2 x Samtec LSHM
  • SPI Flash for booting
  • JTAG 3.3V (via level shifter)
  • 6 I/Os in fixed 1.8V bank
  • 8 I/O with 3.3 I/O voltage (via level shifter)
  • 48 IO's in variable VCCIO bank A (1.8V...2.5V)
  • 48 IO's in variable VCCIO bank B
  • 18 IO's in variable VCCIO bank C
  • Gigabit Transceiver pins in the connector, one lane
  • User LED, Green
  • Status LED's Red
  • Power supply DCDC converters
  • 100MHz differential oscillator as output to the B2B connector (for GT clocking)

That's it, this is a mini-spec of the module we have. Will soon be available from Trenz Electronic online shop.

Tuesday, June 6, 2023

Xilinx SPI flash programming with MCU

Sometimes we wish we could program the SPI flash connected to Xilinx FPGA using some MCU. This is easy if the SPI flash pins are accessible. But what can we do if they are not?

Option 1:

If we want we still can use an external MCU for this we need to add "SPI bypass" wires into all designs. SPI flash pins are accessible for the design, we can just wire them to the pins that go to the MCU. We need to use STARTUP directive to get access to the SPI clock, other pins are simply assignable in the constraints.

We have to use JTAG tools once to program the SPI flash with the design that includes the SPI bypass. And later we need to be sure only program images that include the bypass logic. To make it failsafe we can also write a golden image to the end of the SPI flash that we never erase, so if the first image is corrupt the FPGA would still configure with the golden image.

Option 2: We connect the SPI flash to FPGA embedded processor (MicroBlaze) and implement the SPI flash update with Microblaze code, while the external MCU simply gives the code bytes to program into the flash.

Xilinx JTAG configuration with MCU

It is sometimes needed to configure a Xilinx FPGA via JTAG with MCU. This is possible and doable.

Option 1: we look at the XC3SPROG source code and implement similar functions in our code. This is possible if XC3SPROG already supports our FPGA. XC3SPROG has not developed much in the last few years so the FPGA you need may not yet supported. You can try to add support your self.

Option 2: we implement a FULL SVF player function in your code. This is a possible way but you need to be able to create a line buffer that is as large as the SVF file. This is possible when you run embedded Linux, but not doable if you have a microcontroller in your system.

Option 3: This is a method that can be implemented on pretty much any MCU. We split the programming into 3 operations:

  • playback_tms_tdi(constant_buffer_HEADER);
  • playback_tdi( BIT_FILE );
  • playback_tms_tdi(constant_buffer_FOOTER);

Function playback_tms_tdi takes a string of bytes and pushes the lower two bits to TMS and TDI pins of the JTAG interface then toggles TCK

Function playback_tdi takes raw data and pushes it to the TDI pin and toggles TCK, this is where the actual bitstream is sent to the device.

Now we only need the header and footer arrays, right? Ok, we can easily get the header/footer from Xilinx generated SVF file, well in SVF format. We can use a text editor to extract the header/footer portion. But how to get the constant array that is suitable for our simple playback function? For this we will use an SVF player from GitHub! This SVF player parses the SVF file and creates an array with TMS TDI in the lowest bits, exactly what we need for our simple playback functions. We can a bit modify the header and footer SVF before conversion by removing all READ commands we do not need them when we are doing plain bitfile programming.






Wednesday, May 24, 2023

Xilinx SDRAM IP Core

 I have used plain old SDRAM with different Intel (Altera) FPGAs - as Intel used to have IP core support in the Quartus for free. Well, it used to be free, but now it is removed from Quartus Lite (the option is to install from an older version).

But I always thought that using SDRAM with Xilinx is complicated as Xilinx does not offer plain old SDRAM IP core in the IP catalog.

Until today - I was challenged to test SDRAM on the new version of the MEGA65 home computer. So here is how it did go:

First google: "AXI SDRAM IP core"

taking the first hit, it goes to opencores that has link to the GitHub repository

Downloading from GitHub.

Vivado new project, create new peripheral, assigning nets to AXI memory mapped slave, packaging the IP core.

Vivado, add SDRAM IP Core

Vivado, add a new VHDL module - this was needed to have proper tristate buffers.

Vivado add Microblaze, run automation.

Connecting buses, clock and reset and making SDRAM IO's external.


This is what the new IP and tristate module looks like. Synthesize, open the IO window, type in the constraints for the clock, reset, and SDRAM. Starting to Generate Bitstream. Done, starting Vitis, new platform, new application, selecting memory test. Build, debug:

!?! it does not work, there comes memtest starting text then all is frozen. It does not work. What can it be? I did take an untested IP core in the hope it works. I have no intention of debugging this IP core. Looking at the IP core in block design. Ha, reset polarity is wrong! Changing, starting the build. Ready go, starting to debug in Vitis.

And it works!

We now have a free and working solution to support SDRAM on the Xilinx platform!

Easy as that. It did take less than two hours to verify this solution. So SDRAM on MEGA65 R4 is functional! 

It is interesting that it works the way I did it because the original IP core docs show that ODDR primitive has to be used for CLK output with clock inversion. I did not implement this! It is possible pure luck that it worked out of the box.

Adding clock forward block, connecting it to extra clock output from MMCM. Trying a 100MHz clock, working. Trying 133MHz clock working! Trying 166MHz clock, failing :(

Adjusting SDRAM clock phase on the MMCM, setting it to 180 degrees, and voila SDRAM is finally working at 166MHz! Changing SDRAM size to 32M bytes. Testing, working!

OK, trying to give the project for others to test out with the source files. Placing all source code to GitHub, done! Trying myself to open the GitHub project with Vivado 2022.2

OK, some manual tweaking seems to be needed, but with a few minutes of trying the project is compiling again. Seems that the version upgrade from the files provided in github is possible. There is also a MEMT.ELF file that runs 32Mbyte Xilinx memorytest application, it is included as file to initialize the brams so it will start if you program the bit file. Testing new bitfile and working. So the files from GitHub can be used to create working SDRAM test application for MEGA65. What I had todo was adding new IP repository path, changing the ELF file and removing and adding again the RTL modules.







Wednesday, February 8, 2023

HyperRAM test ok on new FPGA board CR00107

 This board CR00107 arrived yesterday from the production downstairs:




Getting LED blinking was simple but does the HyperRAM also work? Testing out the OpenHBMC IP core. Creating the project and changing constraints. And trying out. And it works, memory tests are passing on the AXI HyperRAM. Cool.

Not so cool, there are random errors when running the memory test in the loop. It seems the issue is related to the relation of axi clock to hyperbus clock. If they are the same 100MHz this causes most failures. Using axi clock of 81.81818MHz and we have way fewer failures.

Changing the hyperBus IP core clocking from BUFG to BUFIO/BUFR mode. And this fixed the issue, no more failures, and also with 100MHz axi clock!

Cool, it REALLY works!

Was happy to fast, after one week of continuous testing, the memory test failed. Pretty hard case to troubleshoot a problem that may come once a week. Next tests seem to fail about once a day, but last test has run over two weeks without failing. Complicated.

Friday, November 4, 2022

Xilinx FTDI without a Digilent license

Xilinx FTDI without a Digilent license. Starting from 2022.2 (or earlier?) Vivado distribution includes an FTDI programming utility that makes the FTx232 devices visible in the Vivado hardware manager. So no need anymore for the secret and invisible Digilent license string! UPDATE the invisible license is still needed, it is silently written...

https://docs.xilinx.com/r/en-US/ug908-vivado-programming-debugging/Programming-FTDI-Devices-for-Vivado-Hardware-Manager-Support 

This is nice that we do no longer need to use the licensed FTDI tool to write into the hidden User EEPROM for Xilinx tools to recognize the programmer dongle. Well, the hidden EEPROM is still used and needed, so if we use FT_PROG to write the FT2232 EEPROM, the hidden license will disappear and the programmer will not be recognized anymore.

Interestingly the provided schematic does not provide support for the Zynq SRST pin :(

Interestingly this Xilinx forum post seems to describe some more used pins:



Thursday, October 27, 2022

Configuration of FPGA with MCU.

 How to configure Xilinx FPGA with MCU using JTAG? This is actually pretty simple, pseudocode:

send_header();

send_raw_bistream(bitstream);

send footer();

The functions send_header and send_footer do some TDI/TMS toggling, send_raw_bistream sends raw bitstream data from the bitstream to FPGA TDI pin (note bit order in the bitstream!).

How to implement send_header/send_footer? The easiest and smallest footprint for the MCU is to implement 2 bit playback function, so the pseudocode would look like this:

send_tdi_tms_bits(header_2bit_file);

send_raw_bistream(bitstream);

send_tdi_tms_bits(footer_2bit_file);

How to get the 2 bit sequences for the header/footer? OK, this is one known working and relatively simple approach:

1) You generate with Xilinx Vivado an SVF file for your target device. If you look at the generated SVF you can quickly see three sections there, there is a header, then raw content, then a footer. Look at header/footer only, you do not want to convert to 2bits the raw data.

2) cut out the header and footer with some text editor

3) remove the ID code check lines from the header, you do not need that! And your MCU playback would be WRITE only with no readback or verify functions.

4) get SVF player code from say maybe here: https://github.com/xaxaxa-dev/svfplayer it looks like useable but I have not compiled this code myself

5) modify the SVF player to emit 2 lower bits of each processed byte, this is an easy exercise for anyone with moderate C experience.

6) play header and footer SVF files with your SVF_to_2bitsfile.exe you created in the previous step.

7) implement for your embedded MCU function send_tdi_tms_bits() and send_raw_bistream(). Use the pseudo code from above on your MCU to configure the FPGA.

OK, there is a little bit of tweaking required, you possible need to insert a delay during header playback, and the line RUNTEST 10000 TCK; should possible also be implemented as direct code with your MCU sending 10,000 TCK cycles without change on TDI/TMS.

OK, the idea should be clear. Happy bit toggling!

How to do it on Rasperry Pi PICO? First of all we need wiring, to save time on the documentation we can take the Pico pin mapping from here: https://github.com/phdussud/pico-dirtyJtag/ Already saved some minutes of work :)

Now we need the function send_tdi_tms_bits as this function is rather short compared to the main junk bitstream pushing we can use machine.Pin class it will be slow but it does not matter. We assume that the 2bit file is generated with the svfplayer with TMS being bit 0 and TDI being bit 1 in the saved file.

from machine import Pin

tms = Pin(19, Pin.OUT)   # TMS: create output pin on GPIO19
tms.on()                 # set TMS to "on" (high) level
tdi = Pin(16, Pin.OUT)
tdi.on()
tck = Pin(18, Pin.OUT)
tck.off()
The above creates pins for TCK, TDI, and TMS, as we are WRITE only interface we do not need to assign TDO to anything, we will not use TDO at all.

Now my python skills are not enough :( but let's give some hints how to proceed:

def send_tdi_tms_bits(byte_with_2bits_in_it)
    tms.value(byte_with_2bits_in_it & 1)
    tdi.value(byte_with_2bits_in_it >>1 & 1)
    tck.on()
    tck.off()

ready :) OK I mixed C and python programming(or did I?). This function needs to be called with the bytes our svf_to_bits converter wrote for the header and footer.

Now, what's next? We need to send the bitstream too right? OK, we can do it using the machine.Pin functions but it would be really slow. OK, for the proof of concept it does not matter.

Later we should optimize the bitstream push using the Pico PIO peripheral, some links:



This is now beyond my current python skills, so it is left to the reader to implement the bit sending with PIO.