An FPGA design project integrating a Hummingbird/e203 RISC-V CPU, custom Verilog RTL, dual-port video memory, UART firmware, and a TMDS/DVI transmitter on a Sipeed Tang Primer 20K. The system drives a 1280 × 720 display at 60 Hz, shows color bars after reset, renders a startup message, and then operates as a monochrome UART terminal.
| Project information | Target hardware | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
![]() Tang Primer 20K documentation HDMI reference design |
All baseline and extended requirements defined in the report were implemented.
| Type | Requirement | Result |
|---|---|---|
| Baseline | Generate vertical color strips at power-up | Completed |
| Baseline | Display a predetermined monochrome sentence | Completed |
| Baseline | Receive and display ASCII characters over UART | Completed |
| Extended | Increase the video resolution | 1280 × 720 at 60 Hz |
| Extended | Add terminal behavior | Enter and backspace supported |
| Extended | Run on a Tang Primer 20K and physical HDMI display | Completed |
| Extended | Position text from software | Configurable start cell implemented |
![]() Text mode — firmware-defined text rendered from DPRAM. |
![]() Power-up mode — eight color strips at 1280 × 720 and 60 Hz. |
The extra t in the captured message was deliberately added to confirm that a new ram.hex image had been included in synthesis.
UART RX (0x10013004)
│
▼
e203 CPU + C firmware + 16×16 ASCII bitmap
│ writes to 0x10014004
▼
ICB peripheral ──► address generator ──► DPRAM
│
HDMI timing/scanner ◄────────────────────────┘
│ RGB + sync + data enable
▼
rPLL / clock divider ──► DVI_TX ──► TMDS ──► HDMI display
At reset, HDMI.v displays eight color strips while firmware writes the startup message into DPRAM. After the startup interval, mode_flag selects text mode. The first UART character erases the prompt and resets the terminal write position.
The DPRAM read address is:
((character_row × 40) + character_column) × 16 + bitmap_row
The design retains a 19,200 × 16-bit DPRAM (38.4 KiB). Each logical bitmap pixel is repeated as a 2 × 2 physical block, allowing the existing 16 × 16 glyph memory to drive a 40 × 22 character grid at 1280 × 720.
rtl/ip/my_ip/icb_bus_HDMI.v— memory-mapped ICB display register at0x10014004.rtl/ip/my_ip/address_gen.v— converts bitmap rows and control flags into DPRAM writes.rtl/ip/my_ip/HDMI.v— video timing, color bars, text scanning, and RGB generation.rtl/ip/my_ip/final.v— connects DPRAM, clocks, HDMI logic, and the TMDS transmitter.firmware/hello_world/src/main.c— startup text, UART polling, positioning, and terminal controls.firmware/hello_world/src/bsp/hbird-e200/drivers/bitmap/bitmap.c— 96 ASCII glyphs.sim/sys_tb_top.sv— complete e203 system testbench and VCD setup.gwin_prj/HDMI_fpga_project.gprj— Gowin implementation project usinge203_soc_demoas top module.rtl/ip/my_ip/data.vandtest_*.v— isolated display-path tests used during development.
ASCII values 32–127 are stored as 16 × 16 monochrome glyphs. Each 16-bit value represents one row: 1 means white and 0 means black. The scanner reads from bit 15 to bit 0.
'y' = ASCII 121 → bitmap index 121 - 32 = 89
bitmap[89][13] = 0x6180 = 0b0110000110000000
Bit scan: 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0
Pixels: · █ █ · · · · █ █ · · · · · · ·
This lights logical pixels 1, 2, 7, and 8. Firmware sends row 13 as 0x000D6180: bits [19:16] select the row and bits [15:0] contain its pixels. Bits [31:27] provide startup-delete, backspace, enter, positioning, and terminal-start commands.
Firmware places Press any key to start at cell (8, 20), polls UART address 0x10013004, and writes sixteen bitmap rows per character to the HDMI register at 0x10014004. The first received byte clears the prompt; later bytes are displayed from the top-left terminal area. Enter, backspace, and software positioning are sent through control bits decoded by address_gen.v.
The final simulation connects the e203 CPU, compiled firmware, ICB peripheral, address generator, DPRAM, and HDMI scanner.
At H_cnt = 164 and V_cnt = 47, the captured configuration enters active video. color_cnt_2 selects the first strip and all RGB outputs become 0xFF, producing white.
Here mode_flag = 1, character column 12 selects y, and bitmap row 13 returns 0x6180. The four set bits enable pixel and drive white RGB output at the expected positions. The mode timer was shortened so text mode could be reached within the 30 ms simulation.
UART testing also confirmed that 0x79 (y) traveled through UART, firmware, ICB, and into DPRAM as the expected bitmap data.
- The monitor did not accept the original 640 × 480 mode, so the timing generator was redesigned for 1280 × 720 at 60 Hz.
- With about 100 KiB of free FPGA RAM, the design retained its 38.4 KiB DPRAM and enlarged pixels to 2 × 2 blocks instead of increasing framebuffer size.
- Physical HDMI required a direct 27 MHz board clock, rPLL, divide-by-five pixel clock, and Gowin
DVI_TXfor differential TMDS output. - An asynchronous address generator worked in simulation but synthesized unreliably. The final version is clocked, accounts for DPRAM latency, and waits for stable CPU data before writing.
- Long video simulations were reduced by shortening the startup timer and verifying UART data directly at the DPRAM boundary.
These changes also delivered the extended requirements: higher resolution, enter/backspace terminal controls, physical-board deployment, and software-defined text positioning.
Required tools are a riscv-none-elf GCC toolchain, GNU Make, Icarus Verilog/GTKWave or a Gowin-compatible simulator, and Gowin EDA/Programmer for hardware.
cd firmware/hello_world/Debug
make clean && makeThis generates the ram.hex file loaded by the e203 instruction memory.
cd sim
bash sim_run_sys_tb.sh
vvp wave.out
gtkwave waveout.vcdRun from sim/ so the relative ram.hex path resolves. The original scripts use Gowin device models; encrypted vendor IP may require Gowin EDA. Shorten the cnt_mode threshold to reproduce the text waveform in a 30 ms run, then restore it for hardware.
Open gwin_prj/HDMI_fpga_project.gprj, relink its original absolute source paths if requested, and select:
- Device:
GW2A-LV18PG256C8/I7 - Top module:
e203_soc_demo - Constraints:
gwin_prj/src/HDMI_fpga_project.cst
Run synthesis and place-and-route, then program gwin_prj/impl/pnr/HDMI_fpga_project.fs. Connect HDMI and use UART at 115200 baud to test text input, enter, and backspace.




