Electronics > Projects, Designs, and Technical Stuff

Custom SoC made with an ICE40 FPGA, and an Assembler

(1/2) > >>

Omega Glory:
    This summer between semesters I decided to learn more about FPGAs so I began working on a simple SoC. It consists of an 8-bit CPU, an 80 column VGA graphics card, GPIO and counter/timer peripherals, and a UART, all implemented on an ice40lp8k FPGA. It also comes with an assembler and utilities for loading programs into the internal block memory without having to rerun synthesis and place-and-route.
    The CPU is an 8-bit RISC core, with a Harvard architecture. It has a 16-bit wide instruction memory, an 8-bit wide data memory, and both have a 16-bit address. The CPU has 16 general purpose 8-bit registers along with a 4-bit status register. The processor is not fully pipelined, but does fetch the next instruction while executing the current one. Most instructions execute in a single clock cycle, but a few take two or three. Nextpnr says that it can be clocked up to about 17 or 18MHz, but I've only tested it at 16Mhz.
    The GPU operates in a monochrome 80 column text mode, and outputs a VGA signal at a resolution of 640 by 480 at 60 frames per second. The GPU contains an ASCII buffer which the user can write to in order to display messages on the screen. A control register allows the user to set the text to one of 7 colors, and to enable an interrupt to the CPU which fires every time a frame finishes and enters the blanking period. The GPU uses a 25Mhz clock, because it's to derive the synchronization signals from it.
    The assembler is written in Python, and generates code in two passes of the source file. It can handle things like symbol definitions, and has expression evaluation, and some helpful directives.
    Documentation is in progress, but I made some colorful diagrams of the CPU data-path and the GPU, as well as some diagrams showing how the assembly instructions are encoded in binary. If you're curious you can find more info on my GitHub at: https://github.com/ept221/tinySoC.  I would love to hear what you guys think, good or bad. I'm especially interested in what you think are good features or flaws of the instruction set, and how I might improve if I were to make a design in the future. Also, this was my first serious project in Verilog (or FPGAs for that matter), so if you happen to see something terrible while browsing the source, your criticism would be appreciated.

Omega Glory:
Here's a picture of the SoC running a simple demo program, along with the source file, and the debugging output of the assembler. The program polls the UART, and when a char is received, it displays it on the screen, while handling backspaces as delete and displaying a cursor. It also displays each char in binary on the LEDs attached to the GPIO, and echos the char back over the UART. This is a simple demo of most of the features, except it doesn't include the use of counter/timers, or the interrupt system. The Arduino that you see in the picture is only acting as a USB to serial bridge for the FPGA.


--- Code: ---;******************************************************************************
        .define dir_reg, 0x00
        .define port_reg, 0x01
        .define pin_reg, 0x02

        .define uart_baud, 0x0A
        .define uart_ctrl, 0x0B
        .define uart_buffer, 0x0C

        .define gpu_addr, 0x2000
        .define gpu_ctrl_reg, 0x80
;******************************************************************************

        .code

        ldi r0, 0xff            ; set all gpio to output
        out r0, dir_reg

        ldi r0, 0b00011100      ; setup the gpu
        out r0, gpu_ctrl_reg

        ldi r2, gpu_addr[l]     ; setup the pointer to the v-ram
        ldi r3, gpu_addr[h]

stable: in r0, gpu_ctrl_reg     ; wait for gpu clock to become stable
        ani r0, 0x80
        jz stable

        ldi r0, 103             ; set the baud rate to 9600
        out r0, uart_baud

loop1:  in r0, uart_ctrl        ; poll for full rx buffer
        ani r0, 1
        jz loop1               

        in r0, uart_buffer      ; capture the data

loop2:  in r1, uart_ctrl        ; poll for empty tx buffer
        ani r1, 2
        jz loop2
        out r0, uart_buffer     ; echo the char back over the uart

        out r0, port_reg        ; write the data to the gpio port

        cpi r0, 8               ; check if delete was sent
        jnz normal
       
        ldi r0, 32
        srd r0, p2              ; print space to clear cursor, and move back
        ldi r0, 95
        str r0, p2              ; delete char and print cursor
        jmp loop1

normal: sri r0, p2              ; write the data to the screen
        ldi r0, 95
        str r0, p2
        jmp loop1               ; go get another char
;******************************************************************************
--- End code ---

T3sl4co1l:
Big project, well done!

voltsandjolts:
Nice project, very well done.
I predict a bright future and an interesting career ahead of you!

rstofer:
This is a great project!  I wonder if there should be a link to this thread from the FPGA forum.

Navigation

[0] Message Index

[#] Next page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod