{"id":13649303,"url":"https://github.com/lawrie/fpga_pio","last_synced_at":"2025-09-07T00:37:51.250Z","repository":{"id":44427114,"uuid":"334413901","full_name":"lawrie/fpga_pio","owner":"lawrie","description":"An attempt to recreate the RP2040 PIO in an FPGA","archived":false,"fork":false,"pushed_at":"2024-06-06T09:00:49.000Z","size":768,"stargazers_count":291,"open_issues_count":5,"forks_count":30,"subscribers_count":20,"default_branch":"main","last_synced_at":"2024-11-10T00:32:56.310Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Verilog","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lawrie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-30T13:02:37.000Z","updated_at":"2024-11-03T07:22:53.000Z","dependencies_parsed_at":"2024-01-14T11:00:20.113Z","dependency_job_id":"d7ab7072-b48d-426a-99dc-0a2483eb0f08","html_url":"https://github.com/lawrie/fpga_pio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lawrie%2Ffpga_pio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lawrie%2Ffpga_pio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lawrie%2Ffpga_pio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lawrie%2Ffpga_pio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lawrie","download_url":"https://codeload.github.com/lawrie/fpga_pio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250258920,"owners_count":21400997,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:04:56.149Z","updated_at":"2025-04-22T14:31:12.408Z","avatar_url":"https://github.com/lawrie.png","language":"Verilog","funding_links":[],"categories":["Verilog","硬件_其他"],"sub_categories":["网络服务_其他"],"readme":"# FPGA RP2040 PIO\n\n## Introduction\n\nThis is an attempt to recreate the Raspberry Pi RP2040 PIO interface in Verilog.\n\nPIO stands for Progammable I/O, and it is a peripheral that is part of the RP2040 SoC, which is much more flexible than hardware implementations of specific protocols like SPI, I2C, UART etc. It can implement all these protocols and more at high speed and on any GPIO pins.\n\nIt runs in up to 8 special processors, known as State Machines, which are programmed in assembler using a machine language designed specifically for fast cycle-accurate I/O. These processors run independently of the main CPUs.\n\nThis implementation has been done from the specification, without access to any Raspberry Pi HDL. It is currently incomplete, but some programs run in simulation and on open source FPGA boards.\n\nThe current supported boards are the Blackice MX and the Ulx3s.\n\nThe current method of configuring and controlling PIO from a top-level module is different from that used on the RP2040 chip, and will probably be changed for closer compatibility.\n\nFor use by a SoC, e.g. a RISC-V SoC such as SaxonSoc, the appropriate peripheral bus interface would need to be added.\n\nFor use from a host processor, such as one running micropython, an SPI read/write memory interface could be added. This would be a lot slower than a bus interface but speed is not usually an issue for configuration and control. There are usually too few pins between a host processor and the fpga to implement a 32-bit (or even an 8-bit) bus interface.\n\n## Simulation\n\nTo run a program in simulation, clone the repository and do:\n\n```sh\ncd fpga_pio/sim\nmake sim\n```\n\nThat runs the tb.v testbench. You can see the results by opening waves.vcd using gtkwave.\n\nYou can run the other test programs in the sim directory, such as uart_tx.v, by:\n\n```sh\nmake sim TB=uart_tx\n```\n\n## Synthesis\n\nTo run the top.v Verilog file on the Blackice MX board, do:\n\n```sh\ncd blackicemx\nmake prog\n```\n\nFor the Ulxs3 board use the ulx3s directory.\n\nThe current program flashes the red led approximately once per second.\n\nYou can select a different top-level module by, for example:\n\n```sh\nmake clean prog TOP=hello\n```\n\nCurrent working top level modules include: blink, hello, wds812, exec, uart_tx.\n\n## Assembling programs\n\nYou can assemble program using the Adafuit pioasm assembler (used by CircuitPython), by:\n\n```sh\ncd asm\n./compile square.asm square.mem\n```\n\nand then move square.mem to the src/top and/or the sim directory.\n\nThe compiler is currently incomplete and so the .mem files sometimes need modification, e.g when the \"'side_set opt\" option is used.\n\n## Examples\n\n### Blink\n\n```\n.program blink\n    set pindirs 1\nagain:\n    set pins 1 [31]  ; Drive pin high and then delay for 31 cycles\n    nop [31]\n    nop [31]\n    nop [31]\n    nop [31]\n    set pins 0 [30]  ; Drive pin low\n    nop [31]\n    nop [31]\n    nop [31]\n    nop [31]\n    jmp again\n```\n\nThis blinks every 320 cycles. so with a maximum clock divider of 64K -1 (0xFFFF), and a 25MHz FPGA clock, it blinks approximately every 1.2 seconds.\n\n### Hello\n\n```\n.program uart_tx\n.side_set 1 opt\n    pull block    side 1\n    set x 7       side 0 [7]\nagain:\n    out pins 1\n    jmp x-- again        [6]\n```\n\nThis example outputs a message repeatedly on the console.\n\n### Exec\n\nThe exec examples needs no PIO program as it executes a SET PINS instruction immediately. The machine does not need to be enabled.\nThe top/exec.v Verilog module uses immediate execution to blink the led approximately once per second.\n\n### Pwm\n\n```\n.program pwm\n.side_set 1 opt\n    pull noblock    side 0\n    mov x osr\n    mov y isr\ncountloop:\n    jmp x!=y noset\n    jmp skip        side 1\nnoset:\n    nop\nskip:\n    jmp y-- countloop\n```\n\nThe pwm example set the led off and then increases its brightness, repeatedly.\n\n### Neopixels\n\n```\n.program ws2812\n.side_set 1\n.wrap_target\nbitloop:\n  out x 1        side 0 [1]; Side-set still takes place when instruction stalls\n  jmp !x do_zero side 1 [1]; Branch on the bit we shifted out. Positive pulse\ndo_one:\n  jmp  bitloop   side 1 [1]; Continue driving high, for a long pulse\ndo_zero:\n  nop            side 0\n```\n\n![ws2812 example](https://raw.githubusercontent.com/lawrie/lawrie.github.io/master/images/ws2812.jpg)\n\n### Stepper motor\n\nThis example shows driving a stepper motor with PIO. \n\nYou set the direction by pushing the required phase patterns as a set of 8 4-bit values, and then you push the required number of half steps.\n\nTo start again with a new set of steps, you execute an immediate jump to the start of the program.\n\n```\n.program stepper\n    pull block\n    mov isr osr\n    pull block\n    mov y osr\nouter:\n    mov osr isr \n    set x 6\ninner:\n    out pins 4 [2]\n    jmp x-- inner\n    out pins 4\n    jmp y-- outer\nwrap_target:\n    out pins 4\n    jmp wrap_target\n```\n\nHere is is driving a stepper motor from a Blackice MX:\n\n![blackice mx stepper](https://github.com/lawrie/lawrie.github.io/blob/master/images/stepper_mx.jpg)\n\n### I2S\n\n```\n.program i2s\n.side_set 2\n    pull noblock  side 3\n    mov x osr     side 3\n    set y 14      side 3 [1]\nloop1:\n    out pins 1    side 2 [3]\n    jmp y-- loop1 side 3 [3]\n    out pins 1    side 0 [3]\n    set y 14      side 1 [3]\nloop0:\n    out pins 1    side 0 [3]\n    jmp y-- loop0 side 1 [3]\n    out pins 1    side 2 [3]\n```\n\nThe I2S example (top/i2s.v) plays a wave file in 16-bit 44100Hz stereo, from BRAM to a Digilent I2S Pmod. It works better on the Ulx3s board, which has more BRAM.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flawrie%2Ffpga_pio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flawrie%2Ffpga_pio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flawrie%2Ffpga_pio/lists"}