├── .gitignore ├── LICENSE ├── README.md ├── asm ├── adafruit_pioasm.py ├── add.asm ├── blink.asm ├── compile ├── copybit.asm ├── i2s.asm ├── pwm.asm ├── spi.asm ├── square.asm ├── step2.asm ├── stepper.asm ├── uart_rx.asm ├── uart_tx.asm ├── ws2812.asm └── ws2812b.asm ├── blackicemx ├── Makefile ├── blackicemx.mk └── pio.pcf ├── pio_stepper.py ├── sim ├── Makefile ├── copybit.mem ├── copybit.v ├── exec.v ├── i2s.mem ├── i2s.v ├── pwm.mem ├── pwm.v ├── spi.mem ├── spi.v ├── square.mem ├── square.v ├── stepper.mem ├── stepper.v ├── tb.v ├── test.mem ├── uart_rx.mem ├── uart_rx.v ├── uart_tx.mem ├── uart_tx.v ├── ws2812.mem └── ws2812.v ├── src ├── decoder.v ├── divider.v ├── fifo.v ├── isr.v ├── machine.v ├── osr.v ├── pc.v ├── pio.v ├── scratch.v └── top │ ├── blink.mem │ ├── blink.v │ ├── blink_conf.mem │ ├── copy_conf.mem │ ├── copybit.mem │ ├── copybit.v │ ├── echo.v │ ├── exec.v │ ├── exec_conf.mem │ ├── guitar.wav │ ├── hello.v │ ├── hello_conf.mem │ ├── i2s.mem │ ├── i2s.v │ ├── i2s_conf.mem │ ├── music.mem │ ├── pwm.mem │ ├── pwm.v │ ├── pwm2.v │ ├── pwm_conf.mem │ ├── pwm_conf1.mem │ ├── pwm_conf2.mem │ ├── rx_conf.mem │ ├── sq_conf.mem │ ├── square.mem │ ├── square.v │ ├── st_conf.mem │ ├── stepper.mem │ ├── stepper.v │ ├── tomem.py │ ├── top.v │ ├── tx_conf.mem │ ├── tx_conf2.mem │ ├── uart_rx.mem │ ├── uart_rx.v │ ├── uart_tx.mem │ ├── uart_tx.v │ ├── ws2812.mem │ ├── ws2812.v │ └── ws_conf.mem └── ulx3s ├── Makefile ├── ulx3s.mk └── ulx3s_v20.lpf /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | __pycache__/ 3 | tb 4 | *.vcd 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2022, Lawrie Griffiths 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FPGA RP2040 PIO 2 | 3 | ## Introduction 4 | 5 | This is an attempt to recreate the Raspberry Pi RP2040 PIO interface in Verilog. 6 | 7 | PIO 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. 8 | 9 | It 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. 10 | 11 | This 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. 12 | 13 | The current supported boards are the Blackice MX and the Ulx3s. 14 | 15 | The 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. 16 | 17 | For use by a SoC, e.g. a RISC-V SoC such as SaxonSoc, the appropriate peripheral bus interface would need to be added. 18 | 19 | For 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. 20 | 21 | ## Simulation 22 | 23 | To run a program in simulation, clone the repository and do: 24 | 25 | ```sh 26 | cd fpga_pio/sim 27 | make sim 28 | ``` 29 | 30 | That runs the tb.v testbench. You can see the results by opening waves.vcd using gtkwave. 31 | 32 | You can run the other test programs in the sim directory, such as uart_tx.v, by: 33 | 34 | ```sh 35 | make sim TB=uart_tx 36 | ``` 37 | 38 | ## Synthesis 39 | 40 | To run the top.v Verilog file on the Blackice MX board, do: 41 | 42 | ```sh 43 | cd blackicemx 44 | make prog 45 | ``` 46 | 47 | For the Ulxs3 board use the ulx3s directory. 48 | 49 | The current program flashes the red led approximately once per second. 50 | 51 | You can select a different top-level module by, for example: 52 | 53 | ```sh 54 | make clean prog TOP=hello 55 | ``` 56 | 57 | Current working top level modules include: blink, hello, wds812, exec, uart_tx. 58 | 59 | ## Assembling programs 60 | 61 | You can assemble program using the Adafuit pioasm assembler (used by CircuitPython), by: 62 | 63 | ```sh 64 | cd asm 65 | ./compile square.asm square.mem 66 | ``` 67 | 68 | and then move square.mem to the src/top and/or the sim directory. 69 | 70 | The compiler is currently incomplete and so the .mem files sometimes need modification, e.g when the "'side_set opt" option is used. 71 | 72 | ## Examples 73 | 74 | ### Blink 75 | 76 | ``` 77 | .program blink 78 | set pindirs 1 79 | again: 80 | set pins 1 [31] ; Drive pin high and then delay for 31 cycles 81 | nop [31] 82 | nop [31] 83 | nop [31] 84 | nop [31] 85 | set pins 0 [30] ; Drive pin low 86 | nop [31] 87 | nop [31] 88 | nop [31] 89 | nop [31] 90 | jmp again 91 | ``` 92 | 93 | This 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. 94 | 95 | ### Hello 96 | 97 | ``` 98 | .program uart_tx 99 | .side_set 1 opt 100 | pull block side 1 101 | set x 7 side 0 [7] 102 | again: 103 | out pins 1 104 | jmp x-- again [6] 105 | ``` 106 | 107 | This example outputs a message repeatedly on the console. 108 | 109 | ### Exec 110 | 111 | The exec examples needs no PIO program as it executes a SET PINS instruction immediately. The machine does not need to be enabled. 112 | The top/exec.v Verilog module uses immediate execution to blink the led approximately once per second. 113 | 114 | ### Pwm 115 | 116 | ``` 117 | .program pwm 118 | .side_set 1 opt 119 | pull noblock side 0 120 | mov x osr 121 | mov y isr 122 | countloop: 123 | jmp x!=y noset 124 | jmp skip side 1 125 | noset: 126 | nop 127 | skip: 128 | jmp y-- countloop 129 | ``` 130 | 131 | The pwm example set the led off and then increases its brightness, repeatedly. 132 | 133 | ### Neopixels 134 | 135 | ``` 136 | .program ws2812 137 | .side_set 1 138 | .wrap_target 139 | bitloop: 140 | out x 1 side 0 [1]; Side-set still takes place when instruction stalls 141 | jmp !x do_zero side 1 [1]; Branch on the bit we shifted out. Positive pulse 142 | do_one: 143 | jmp bitloop side 1 [1]; Continue driving high, for a long pulse 144 | do_zero: 145 | nop side 0 146 | ``` 147 | 148 | ![ws2812 example](https://raw.githubusercontent.com/lawrie/lawrie.github.io/master/images/ws2812.jpg) 149 | 150 | ### Stepper motor 151 | 152 | This example shows driving a stepper motor with PIO. 153 | 154 | You 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. 155 | 156 | To start again with a new set of steps, you execute an immediate jump to the start of the program. 157 | 158 | ``` 159 | .program stepper 160 | pull block 161 | mov isr osr 162 | pull block 163 | mov y osr 164 | outer: 165 | mov osr isr 166 | set x 6 167 | inner: 168 | out pins 4 [2] 169 | jmp x-- inner 170 | out pins 4 171 | jmp y-- outer 172 | wrap_target: 173 | out pins 4 174 | jmp wrap_target 175 | ``` 176 | 177 | Here is is driving a stepper motor from a Blackice MX: 178 | 179 | ![blackice mx stepper](https://github.com/lawrie/lawrie.github.io/blob/master/images/stepper_mx.jpg) 180 | 181 | ### I2S 182 | 183 | ``` 184 | .program i2s 185 | .side_set 2 186 | pull noblock side 3 187 | mov x osr side 3 188 | set y 14 side 3 [1] 189 | loop1: 190 | out pins 1 side 2 [3] 191 | jmp y-- loop1 side 3 [3] 192 | out pins 1 side 0 [3] 193 | set y 14 side 1 [3] 194 | loop0: 195 | out pins 1 side 0 [3] 196 | jmp y-- loop0 side 1 [3] 197 | out pins 1 side 2 [3] 198 | ``` 199 | 200 | The 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. 201 | -------------------------------------------------------------------------------- /asm/adafruit_pioasm.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries LLC 2 | # 3 | # SPDX-License-Identifier: MIT 4 | """ 5 | `adafruit_pioasm` 6 | ================================================================================ 7 | 8 | Simple assembler to convert pioasm to bytes 9 | 10 | 11 | * Author(s): Scott Shawcroft 12 | """ 13 | 14 | import array 15 | 16 | __version__ = "0.0.0-auto.0" 17 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PIOASM.git" 18 | 19 | CONDITIONS = ["", "!x", "x--", "!y", "y--", "x!=y", "pin", "!osre"] 20 | IN_SOURCES = ["pins", "x", "y", "null", None, None, "isr", "osr"] 21 | OUT_DESTINATIONS = ["pins", "x", "y", "null", "pindirs", "pc", "isr", "exec"] 22 | WAIT_SOURCES = ["gpio", "pin", "irq", None] 23 | MOV_DESTINATIONS = ["pins", "x", "y", None, "exec", "pc", "isr", "osr"] 24 | MOV_SOURCES = ["pins", "x", "y", "null", None, "status", "isr", "osr"] 25 | MOV_OPS = [None, "~", "::", None] 26 | SET_DESTINATIONS = ["pins", "x", "y", None, "pindirs", None, None, None] 27 | 28 | def assemble(text_program): 29 | """Converts pioasm text to encoded instruction bytes""" 30 | assembled = [] 31 | program_name = None 32 | labels = {} 33 | instructions = [] 34 | sideset_count = 0 35 | for line in text_program.split("\n"): 36 | line = line.strip() 37 | if not line: 38 | continue 39 | if ";" in line: 40 | line = line.split(";")[0].strip() 41 | if line.startswith(".program"): 42 | if program_name: 43 | raise RuntimeError("Multiple programs not supported") 44 | program_name = line.split()[1] 45 | elif line.startswith(".wrap_target"): 46 | if len(instructions) > 0: 47 | raise RuntimeError("wrap_target not supported") 48 | elif line.startswith(".wrap"): 49 | pass 50 | elif line.startswith(".side_set"): 51 | sideset_count = int(line.split()[1]) 52 | elif line.endswith(":"): 53 | labels[line[:-1]] = len(instructions) 54 | else: 55 | instructions.append(line) 56 | 57 | max_delay = 2 ** (5 - sideset_count) - 1 58 | assembled = [] 59 | for instruction in instructions: 60 | print(instruction) 61 | instruction = instruction.split() 62 | delay = 0 63 | if instruction[-1].endswith("]"): # Delay 64 | delay = int(instruction[-1].strip("[]")) 65 | if delay > max_delay: 66 | raise RuntimeError("Delay too long:", delay) 67 | instruction.pop() 68 | if len(instruction) > 1 and instruction[-2] == "side": 69 | sideset_value = int(instruction[-1]) 70 | if sideset_value > 2 ** sideset_count: 71 | raise RuntimeError("Sideset value too large") 72 | delay |= sideset_value << (5 - sideset_count) 73 | instruction.pop() 74 | instruction.pop() 75 | 76 | if instruction[0] == "nop": 77 | # mov delay y op y 78 | assembled.append(0b101_00000_010_00_010) 79 | elif instruction[0] == "jmp": 80 | # instr delay cnd addr 81 | assembled.append(0b000_00000_000_00000) 82 | if instruction[-1] in labels: 83 | assembled[-1] |= labels[instruction[-1]] 84 | else: 85 | assembled[-1] |= int(instruction[-1]) 86 | 87 | if len(instruction) > 2: 88 | assembled[-1] |= CONDITIONS.index(instruction[1]) << 5 89 | 90 | elif instruction[0] == "wait": 91 | # instr delay p sr index 92 | assembled.append(0b001_00000_0_00_00000) 93 | polarity = int(instruction[1]) 94 | if not 0 <= polarity <= 1: 95 | raise RuntimeError("Invalid polarity") 96 | assembled[-1] |= polarity << 7 97 | assembled[-1] |= WAIT_SOURCES.index(instruction[2]) << 4 98 | num = int(instruction[3]) 99 | if not 0 <= num <= 31: 100 | raise RuntimeError("Wait num out of range") 101 | assembled[-1] |= num 102 | if instruction[-1] == "rel": 103 | assembled[-1] |= 0x10 # Set the high bit of the irq value 104 | elif instruction[0] == "in": 105 | # instr delay src count 106 | assembled.append(0b010_00000_000_00000) 107 | assembled[-1] |= IN_SOURCES.index(instruction[1]) << 5 108 | count = int(instruction[-1]) 109 | if not 1 <= count <=32: 110 | raise RuntimeError("Count out of range") 111 | assembled[-1] |= (count & 0x1f) # 32 is 00000 so we mask the top 112 | elif instruction[0] == "out": 113 | # instr delay dst count 114 | assembled.append(0b011_00000_000_00000) 115 | assembled[-1] |= OUT_DESTINATIONS.index(instruction[1]) << 5 116 | count = int(instruction[-1]) 117 | if not 1 <= count <=32: 118 | raise RuntimeError("Count out of range") 119 | assembled[-1] |= (count & 0x1f) # 32 is 00000 so we mask the top 120 | elif instruction[0] == "push" or instruction[0] == "pull": 121 | # instr delay d i b zero 122 | assembled.append(0b100_00000_0_0_0_00000) 123 | if instruction[0] == "pull": 124 | assembled[-1] |= 0x80 125 | if instruction[-1] == "block" or not instruction[-1].endswith("block"): 126 | assembled[-1] |= 0x20 127 | if instruction[1] in ("ifempty", "iffull"): 128 | assembled[-1] |= 0x40 129 | elif instruction[0] == "mov": 130 | # instr delay dst op src 131 | assembled.append(0b101_00000_000_00_000) 132 | assembled[-1] |= MOV_DESTINATIONS.index(instruction[1]) << 5 133 | assembled[-1] |= MOV_SOURCES.index(instruction[-1]) 134 | if len(instruction) > 3: 135 | assembled[-1] |= MOV_OPS.index(instruction[-2]) 136 | elif instruction[0] == "irq": 137 | # instr delay z c w index 138 | assembled.append(0b110_00000_0_0_0_00000) 139 | if instruction[-1] == "rel": 140 | assembled[-1] |= 0x10 # Set the high bit of the irq value 141 | instruction.pop() 142 | num = int(instruction[-1]) 143 | if not 0 <= num <= 7: 144 | raise RuntimeError("Interrupt index out of range") 145 | assembled[-1] |= num 146 | if len(instruction) == 3: # after rel has been removed 147 | if instruction[1] == "wait": 148 | assembled[-1] |= 0x20 149 | elif instruction[1] == "clear": 150 | assembled[-1] |= 0x40 151 | # All other values are the default of set without waiting 152 | elif instruction[0] == "set": 153 | # instr delay dst data 154 | assembled.append(0b111_00000_000_00000) 155 | assembled[-1] |= SET_DESTINATIONS.index(instruction[1]) << 5 156 | value = int(instruction[-1]) 157 | if not 0 <= value <=31: 158 | raise RuntimeError("Set value out of range") 159 | assembled[-1] |= value 160 | else: 161 | raise RuntimeError("Unknown instruction:" + instruction) 162 | assembled[-1] |= delay << 8 163 | print(hex(assembled[-1])) 164 | 165 | return array.array("H", assembled) 166 | -------------------------------------------------------------------------------- /asm/add.asm: -------------------------------------------------------------------------------- 1 | .program add 2 | pull block 3 | mov x ~osr 4 | pull block 5 | mov y osr 6 | jmp test 7 | incr: 8 | jmp x-- test 9 | test: 10 | jmp y-- incr 11 | mv isr ~x 12 | push 13 | 14 | -------------------------------------------------------------------------------- /asm/blink.asm: -------------------------------------------------------------------------------- 1 | .program blink 2 | set pindirs 1 3 | again: 4 | set pins 1 [31] ; Drive pin high and then delay for 31 cycles 5 | nop [31] 6 | nop [31] 7 | nop [31] 8 | nop [31] 9 | set pins 0 [30] ; Drive pin low 10 | nop [31] 11 | nop [31] 12 | nop [31] 13 | nop [31] 14 | jmp again 15 | 16 | -------------------------------------------------------------------------------- /asm/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from adafruit_pioasm import assemble 4 | import sys 5 | 6 | args=sys.argv 7 | 8 | if len(args) < 3: 9 | print("Usage: " + args[0] + " ") 10 | sys.exit(1) 11 | 12 | f=open(args[1],"r"); 13 | text = f.read(); 14 | f.close() 15 | 16 | bin=assemble(text) 17 | 18 | f=open(args[2],"w") 19 | 20 | for i in bin: 21 | h = format(i,"04x") 22 | f.write(h + "\n") 23 | 24 | f.close() 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /asm/copybit.asm: -------------------------------------------------------------------------------- 1 | .program copybit 2 | pull block 3 | out pins 1 4 | 5 | -------------------------------------------------------------------------------- /asm/i2s.asm: -------------------------------------------------------------------------------- 1 | .program i2s 2 | .side_set 2 3 | pull noblock side 3 4 | mov x osr side 3 5 | set y 14 side 3 [1] 6 | loop1: 7 | out pins 1 side 2 [3] 8 | jmp y-- loop1 side 3 [3] 9 | out pins 1 side 0 [3] 10 | set y 14 side 1 [3] 11 | loop0: 12 | out pins 1 side 0 [3] 13 | jmp y-- loop0 side 1 [3] 14 | out pins 1 side 2 [3] 15 | 16 | -------------------------------------------------------------------------------- /asm/pwm.asm: -------------------------------------------------------------------------------- 1 | .program pwm 2 | .side_set 1 opt 3 | pull noblock side 0 4 | mov x osr 5 | mov y isr 6 | countloop: 7 | jmp x!=y noset 8 | jmp skip side 1 9 | noset: 10 | nop 11 | skip: 12 | jmp y-- countloop 13 | 14 | -------------------------------------------------------------------------------- /asm/spi.asm: -------------------------------------------------------------------------------- 1 | .program spi 2 | .side_set 1 3 | out pins 1 side 0 [1] 4 | in pins 1 side 1 [1] 5 | 6 | -------------------------------------------------------------------------------- /asm/square.asm: -------------------------------------------------------------------------------- 1 | .program square 2 | set pins 1 ; Drive pin high 3 | set pins 0 ; Drive pin low 4 | 5 | -------------------------------------------------------------------------------- /asm/step2.asm: -------------------------------------------------------------------------------- 1 | .program stepper 2 | pull block 3 | mov isr osr 4 | pull block 5 | mov y osr 6 | outer: 7 | mov osr isr 8 | set x 6 9 | inner: 10 | out pins 4 [2] 11 | jmp x-- inner 12 | out pins 4 13 | jmp y-- outer 14 | wrap_target: 15 | out pins 4 16 | jmp wrap_target 17 | 18 | -------------------------------------------------------------------------------- /asm/stepper.asm: -------------------------------------------------------------------------------- 1 | .program stepper 2 | mov osr isr 3 | set x 6 4 | loop: 5 | out pins 4 [1] 6 | jmp x-- loop 7 | out pins 4 8 | 9 | 10 | -------------------------------------------------------------------------------- /asm/uart_rx.asm: -------------------------------------------------------------------------------- 1 | .program uart_rx 2 | wait 0 pin 0 3 | set x 7 [10] 4 | again: 5 | in pins 1 6 | jmp x-- again [6] 7 | 8 | -------------------------------------------------------------------------------- /asm/uart_tx.asm: -------------------------------------------------------------------------------- 1 | .program uart_tx 2 | .side_set 1 opt 3 | pull block side 1 4 | set x 7 side 0 [7] 5 | again: 6 | out pins 1 7 | jmp x-- again [6] 8 | -------------------------------------------------------------------------------- /asm/ws2812.asm: -------------------------------------------------------------------------------- 1 | .program ws2812 2 | .side_set 1 3 | .wrap_target 4 | bitloop: 5 | out x 1 side 0 [1] ; Side-set still takes place when instruction stalls 6 | jmp !x do_zero side 1 [1] ; Branch on the bit we shifted out. Positive pulse 7 | do_one: 8 | jmp bitloop side 1 [1] ; Continue driving high, for a long pulse 9 | do_zero: 10 | nop side 0 [1] ; Or drive low for a short pulse 11 | 12 | -------------------------------------------------------------------------------- /asm/ws2812b.asm: -------------------------------------------------------------------------------- 1 | .program ws2812 2 | .side_set 1 3 | .wrap_target 4 | bitloop: 5 | out x 1 side 0 [2]; Side-set still takes place when instruction stalls 6 | jmp !x do_zero side 1 [1]; Branch on the bit we shifted out. Positive pulse 7 | do_one: 8 | jmp bitloop side 1 [4]; Continue driving high, for a long pulse 9 | do_zero: 10 | nop side 0 [4] 11 | 12 | -------------------------------------------------------------------------------- /blackicemx/Makefile: -------------------------------------------------------------------------------- 1 | TOP ?= top 2 | 3 | VERILOG = ../src/top/${TOP}.v ../src/pio.v ../src/machine.v \ 4 | ../src/pc.v ../src/decoder.v ../src/divider.v \ 5 | ../src/isr.v ../src/osr.v ../src/scratch.v ../src/fifo.v 6 | 7 | PCF_FILE = pio.pcf 8 | 9 | include blackicemx.mk 10 | 11 | -------------------------------------------------------------------------------- /blackicemx/blackicemx.mk: -------------------------------------------------------------------------------- 1 | bin/toplevel.bin : bin/toplevel.asc 2 | icepack bin/toplevel.asc bin/toplevel.bin 3 | 4 | bin/toplevel.json : ${VERILOG} 5 | mkdir -p bin 6 | yosys -q -f "verilog -Dblackicemx" -p "synth_ice40 -abc9 -json bin/toplevel.json" ${VERILOG} 7 | 8 | bin/toplevel.asc : ${PCF_FILE} bin/toplevel.json 9 | nextpnr-ice40 --freq 18 --pcf-allow-unconstrained --hx8k --package tq144:4k --json bin/toplevel.json --pcf ${PCF_FILE} --asc bin/toplevel.asc --opt-timing --placer heap 10 | 11 | .PHONY: time 12 | time: bin/toplevel.bin 13 | icetime -tmd hx8k bin/toplevel.asc 14 | 15 | .PHONY: prog 16 | prog: bin/toplevel.bin 17 | stty -F /dev/ttyACM0 raw 18 | cat bin/toplevel.bin >/dev/ttyACM0 19 | 20 | .PHONY: clean 21 | clean : 22 | rm -rf bin 23 | 24 | -------------------------------------------------------------------------------- /blackicemx/pio.pcf: -------------------------------------------------------------------------------- 1 | set_io clk_25mhz 60 2 | set_io -pullup yes btn[0] 49 3 | 4 | set_io led[0] 56 5 | set_io led[1] 55 6 | set_io led[2] 52 7 | 8 | set_io gn[0] 26 9 | set_io gn[1] 25 10 | set_io gn[2] 22 11 | set_io gn[3] 21 12 | 13 | set_io tx 62 14 | set_io rx 61 15 | 16 | 17 | -------------------------------------------------------------------------------- /pio_stepper.py: -------------------------------------------------------------------------------- 1 | import time 2 | import rp2 3 | from machine import Pin 4 | 5 | @rp2.asm_pio(out_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW)) 6 | def stepper(): 7 | pull() 8 | mov(isr, osr) 9 | pull() 10 | mov(y, osr) 11 | label("outer") 12 | mov(osr, isr) 13 | set(x, 6) 14 | label("inner") 15 | out(pins, 4) [2] 16 | jmp(x_dec, "inner") 17 | out(pins, 4) 18 | jmp(y_dec, "outer") 19 | wrap_target() 20 | out(pins, 4) 21 | wrap() 22 | 23 | # Instantiate a state machine with the stepper program, at 2000Hz. 24 | forward = 0b10001100010001100010001100011001 25 | 26 | sm = rp2.StateMachine(0, stepper, freq=2000, out_base=Pin(16)) 27 | 28 | # Run the state machine 29 | sm.active(1) 30 | # Set the direction to forwards 31 | sm.put(forward) 32 | # Set the count of half steps to 1 million 33 | sm.put(1000000) 34 | -------------------------------------------------------------------------------- /sim/Makefile: -------------------------------------------------------------------------------- 1 | VERILOG_FILES = ../src/pio.v ../src/machine.v ../src/decoder.v ../src/divider.v ../src/pc.v \ 2 | ../src/scratch.v ../src/fifo.v ../src/isr.v ../src/osr.v 3 | TB ?= tb 4 | 5 | ${TB}: *.v 6 | iverilog -o tb ${TB}.v ${VERILOG_FILES} 7 | 8 | sim: ${TB} 9 | ./tb 10 | 11 | clean: 12 | rm tb 13 | 14 | -------------------------------------------------------------------------------- /sim/copybit.mem: -------------------------------------------------------------------------------- 1 | 80a0 2 | 6001 3 | -------------------------------------------------------------------------------- /sim/copybit.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/100ps 2 | module tb(); 3 | 4 | initial begin 5 | $dumpfile("waves.vcd"); 6 | $dumpvars(0, tb); 7 | end 8 | 9 | // Clock generation 10 | reg clk; 11 | reg reset; 12 | 13 | initial begin 14 | clk = 1'b0; 15 | end 16 | 17 | // 25MHz clock 18 | always begin 19 | #20 clk = !clk; 20 | end 21 | 22 | // PIO inputs 23 | reg [31:0] din; 24 | reg [4:0] index; 25 | reg [3:0] action; 26 | reg [1:0] mindex; 27 | reg [31:0] gpio_in = 0; 28 | 29 | // PIO outputs 30 | wire [31:0] gpio_out; 31 | wire[31:0] gpio_dir; 32 | wire [31:0] dout; 33 | wire [3:0] tx_full; 34 | wire [3:0] rx_empty; 35 | 36 | // Configuration 37 | reg [15:0] program [0:31]; 38 | initial $readmemh("copybit.mem", program); 39 | 40 | wire [5:0] plen = 2; // Program length 41 | wire [23:0] div = 24'h0100; // Clock divider 42 | wire [31:0] pin_grps = 32'h100000; // OUT group is pin 0# 43 | wire [31:0] exec_ctrl = 32'h00001000; // Wrap top 44 | 45 | integer i; 46 | 47 | // Actions 48 | localparam NONE = 0; 49 | localparam INSTR = 1; 50 | localparam PEND = 2; 51 | localparam PULL = 3; 52 | localparam PUSH = 4; 53 | localparam GRPS = 5; 54 | localparam EN = 6; 55 | localparam DIV = 7; 56 | localparam SIDES = 8; 57 | localparam IMM = 9; 58 | localparam SHIFT = 10; 59 | 60 | // Task to send action to PIO 61 | task act ( 62 | input [3:0] a, 63 | input [31:0] d 64 | ); 65 | begin 66 | @(negedge clk); 67 | action = a; 68 | din = d; 69 | @(posedge clk); 70 | end 71 | endtask 72 | 73 | // Configure and run the PIO program 74 | initial begin 75 | // Do reset 76 | reset = 1'b1; 77 | repeat(2) @(posedge clk); 78 | reset = 1'b0; 79 | 80 | // Set the instructions 81 | for(i=0;i 0) begin 103 | act(IMM, offset); 104 | end 105 | 106 | // Enable machine 1 107 | act(EN, 1); 108 | 109 | // Configuration done 110 | act(NONE, 0); 111 | 112 | // Small gap 113 | repeat(2) @(posedge clk); 114 | 115 | // Send data 116 | for(i=0;i<10;i++) begin 117 | // Send numeric character 118 | act(PUSH, 32'h30 + i); 119 | action = 0; 120 | 121 | // Wait for it to be sent 122 | repeat(180) @(posedge clk); 123 | end 124 | 125 | $finish; 126 | end 127 | 128 | pio pio_1 ( 129 | .clk(clk), 130 | .reset(reset), 131 | .action(action), 132 | .index(index), 133 | .mindex(mindex), 134 | .din(din), 135 | .dout(dout), 136 | .gpio_in(gpio_in), 137 | .gpio_out(gpio_out), 138 | .gpio_dir(gpio_dir), 139 | .tx_full(tx_full), 140 | .rx_empty(rx_empty) 141 | ); 142 | 143 | endmodule 144 | 145 | -------------------------------------------------------------------------------- /sim/ws2812.mem: -------------------------------------------------------------------------------- 1 | 6121 2 | 1123 3 | 1100 4 | a142 5 | -------------------------------------------------------------------------------- /sim/ws2812.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/100ps 2 | module tb(); 3 | 4 | initial begin 5 | $dumpfile("waves.vcd"); 6 | $dumpvars(0, tb); 7 | end 8 | 9 | // Clock generation 10 | reg clk; 11 | reg reset; 12 | 13 | initial begin 14 | clk = 1'b0; 15 | end 16 | 17 | // 25MHz clock 18 | always begin 19 | #20 clk = !clk; 20 | end 21 | 22 | // PIO inputs 23 | reg [31:0] din; 24 | reg [4:0] index; 25 | reg [3:0] action; 26 | reg [1:0] mindex; 27 | reg [31:0] gpio_in = 0; 28 | 29 | // PIO outputs 30 | wire [31:0] gpio_out; 31 | wire[31:0] gpio_dir; 32 | wire [31:0] dout; 33 | wire [3:0] tx_full; 34 | wire [3:0] rx_empty; 35 | 36 | // Configuration 37 | reg [15:0] program [0:31]; 38 | initial $readmemh("ws2812.mem", program); 39 | 40 | wire [5:0] plen = 4; // Program length 41 | wire [23:0] div = 24'h0535; // Clock divider 25M / (6 * 800K) 42 | wire [31:0] pin_grps = 32'h20000000; // SIDE grp pin 0 43 | wire [31:0] exec_ctrl = 32'h00003000; // Wrap top 44 | 45 | integer i; 46 | 47 | // Actions 48 | localparam NONE = 0; 49 | localparam INSTR = 1; 50 | localparam PEND = 2; 51 | localparam PULL = 3; 52 | localparam PUSH = 4; 53 | localparam GRPS = 5; 54 | localparam EN = 6; 55 | localparam DIV = 7; 56 | localparam SIDES = 8; 57 | localparam IMM = 9; 58 | localparam SHIFT = 10; 59 | 60 | // Task to send action to PIO 61 | task act ( 62 | input [3:0] a, 63 | input [31:0] d 64 | ); 65 | begin 66 | @(negedge clk); 67 | action = a; 68 | din = d; 69 | @(posedge clk); 70 | end 71 | endtask 72 | 73 | // Configure and run the PIO program 74 | initial begin 75 | // Do reset 76 | reset = 1'b1; 77 | repeat(2) @(posedge clk); 78 | reset = 1'b0; 79 | 80 | // Set the instructions 81 | for(i=0;i> sideset_bits; 23 | assign side_set = instr[12:8] << sideset_enable_bit >> sideset_enable_bit + delay_bits; 24 | assign sideset_enabled = sideset_enable_bit ? instr[12] : 1; 25 | 26 | endmodule 27 | 28 | -------------------------------------------------------------------------------- /src/divider.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module divider ( 6 | input clk, 7 | input reset, 8 | input [23:0] div, 9 | input use_divider, 10 | output penable, 11 | output pclk 12 | ); 13 | 14 | reg [23:0] div_counter; 15 | reg pen; 16 | reg old_pen; 17 | 18 | always @(posedge clk) begin 19 | if (reset) begin 20 | div_counter <= 0; 21 | pen <= 1; 22 | old_pen <= 0; 23 | end else begin 24 | if (use_divider) begin 25 | old_pen <= pen; 26 | div_counter <= div_counter + 256; 27 | if (div_counter >= div - 256) 28 | div_counter <= div_counter - (div - 256); 29 | pen <= div_counter < (div >> 1); 30 | end 31 | end 32 | end 33 | 34 | assign penable = pen & ~old_pen; 35 | assign pclk = pen; 36 | 37 | endmodule 38 | -------------------------------------------------------------------------------- /src/fifo.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module fifo ( 6 | input clk, 7 | input reset, 8 | input push, 9 | input pull, 10 | input [31:0] din, 11 | output [31:0] dout, 12 | output empty, 13 | output full, 14 | output [2:0] level 15 | ); 16 | 17 | reg [31:0] arr [0:3]; 18 | reg [1:0] first; 19 | reg [1:0] next; 20 | reg [2:0] count; 21 | 22 | wire do_pull = pull && !empty; 23 | wire do_push = push && !full; 24 | 25 | always @(posedge clk) begin 26 | if (reset) begin 27 | first <= 0; 28 | next <= 0; 29 | count <= 0; 30 | end else begin 31 | if (do_push) begin 32 | next <= next + 1; 33 | arr[next] <= din; 34 | if (!do_pull) count <= count + 1; 35 | end 36 | if (do_pull) begin 37 | first <= first + 1; 38 | if (!do_push) count <= count - 1; 39 | end 40 | end 41 | end 42 | 43 | assign empty = count == 0; 44 | assign full = count == 4; 45 | assign dout = arr[first]; 46 | assign level = count; 47 | 48 | endmodule 49 | -------------------------------------------------------------------------------- /src/isr.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module isr ( 6 | input clk, 7 | input penable, 8 | input reset, 9 | input stalled, 10 | input [31:0] din, 11 | input [4:0] shift, 12 | input dir, 13 | input set, 14 | input do_shift, 15 | input [5:0] bit_count, 16 | output [31:0] dout, 17 | output [5:0] shift_count 18 | ); 19 | 20 | reg [31:0] shift_reg; 21 | reg [5:0] count; 22 | 23 | // A shift value of 0 means shift 32 24 | wire [5:0] shift_val = shift == 0 ? 32 : shift; 25 | // Left align the input value and concatenate it with the shift register to produce a 64-bit value 26 | wire [63:0] new_shift = dir ? {din, shift_reg} >> shift_val 27 | : {shift_reg, din << (32 - shift_val)} << shift_val; 28 | 29 | always @(posedge clk) begin 30 | if (reset) begin 31 | shift_reg <= 0; 32 | count <= 0; // Empty 33 | end else if (penable && !stalled) begin 34 | if (set) begin 35 | shift_reg <= din; 36 | count <= bit_count; 37 | end else if (do_shift) begin 38 | shift_reg <= dir ? new_shift[31:0] : new_shift[63:32]; 39 | count <= count + shift_val > 32 ? 32 : count + shift_val; 40 | end 41 | end 42 | end 43 | 44 | assign dout = shift_reg; 45 | assign shift_count = count; 46 | 47 | endmodule 48 | 49 | -------------------------------------------------------------------------------- /src/machine.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module machine ( 6 | input clk, 7 | input reset, 8 | input en, 9 | input [23:0] div, 10 | input use_divider, 11 | input [31:0] din, 12 | input [15:0] instr, 13 | input [31:0] input_pins, 14 | input [7:0] irq_flags_in, 15 | input imm, 16 | input empty, 17 | input full, 18 | input restart, 19 | 20 | // Configuration 21 | input [1:0] mindex, 22 | input [4:0] pend, 23 | input [4:0] wrap_target, 24 | input [4:0] jmp_pin, 25 | input sideset_enable_bit, 26 | input [4:0] pins_out_base, 27 | input [5:0] pins_out_count, 28 | input [4:0] pins_set_base, 29 | input [2:0] pins_set_count, 30 | input [4:0] pins_in_base, 31 | input [4:0] pins_side_base, 32 | input [2:0] pins_side_count, 33 | input out_shift_dir, 34 | input in_shift_dir, 35 | input auto_pull, 36 | input auto_push, 37 | input [4:0] isr_threshold, 38 | input [4:0] osr_threshold, 39 | 40 | // Output 41 | output [4:0] pc, 42 | output reg push, // Send data to RX FIFO 43 | output reg pull, // Get data from TX FIFO 44 | output reg [31:0] dout, 45 | output reg [31:0] output_pins, 46 | output reg [31:0] pin_directions, 47 | output reg [7:0] irq_flags_out, 48 | output pclk 49 | ); 50 | 51 | // Strobes to implement instructions (combinatorial) 52 | reg jmp; 53 | reg setx; 54 | reg sety; 55 | reg decx; 56 | reg decy; 57 | reg set_shift_in; 58 | reg set_shift_out; 59 | reg do_in_shift; 60 | reg do_out_shift; 61 | reg set_set_pins; 62 | reg set_set_dirs; 63 | reg set_out_pins; 64 | reg set_out_dirs; 65 | reg set_side_pins; 66 | reg set_side_dirs; 67 | reg exec; 68 | reg waiting; 69 | reg auto; 70 | 71 | reg [5:0] bit_count; 72 | reg [31:0] new_val; 73 | 74 | reg exec1 = 0; 75 | reg [15:0] exec_instr; 76 | 77 | // Divided clock enable signal 78 | wire penable; 79 | 80 | // Output from modules 81 | wire [31:0] x; 82 | wire [31:0] y; 83 | wire [31:0] in_shift; 84 | wire [31:0] out_shift; 85 | wire [2:0] op; 86 | wire [2:0] op1; 87 | wire [4:0] op2; 88 | wire [4:0] delay; 89 | wire [4:0] side_set; 90 | wire sideset_enabled; 91 | 92 | // Names of operands 93 | wire blocking = op1[0]; 94 | wire if_full = op1[1]; 95 | wire if_empty = op1[1]; 96 | wire [2:0] destination = op1; 97 | wire [2:0] source = op1; 98 | wire [2:0] condition = op1; 99 | wire [1:0] source2 = op1[1:0]; 100 | wire polarity = op1[2]; 101 | wire [4:0] index = op2; 102 | wire [4:0] address = op2; 103 | wire [4:0] data = op2; 104 | wire [1:0] irq_rel = op2[4] ? mindex + op2[1:0] : op2[1:0]; 105 | wire [2:0] irq_index = {op2[2], irq_rel}; 106 | wire [2:0] mov_source = op2[2:0]; 107 | wire [1:0] mov_op = op2[4:3]; 108 | 109 | // Miscellaneous signals 110 | wire [31:0] null = 0; // NULL source 111 | wire [5:0] isr_count, osr_count; 112 | wire [31:0] in_pins = input_pins << pins_in_base; 113 | 114 | // Values for use in gtkwave during simulation 115 | wire pin0 = output_pins[0]; 116 | wire pin1 = output_pins[1]; 117 | wire pin2 = output_pins[2]; 118 | wire pin3 = output_pins[3]; 119 | 120 | wire in_pin0 = input_pins[0]; 121 | 122 | reg [4:0] delay_cnt = 0; 123 | 124 | // States 125 | wire enabled = exec1 || imm || (en && penable); // Instruction execution enabled 126 | wire delaying = delay_cnt > 0; 127 | 128 | // Function to reverse the order of bits in a word 129 | function [31:0] reverse ( 130 | input [31:0] in 131 | ); 132 | 133 | integer i; 134 | for(i=0;i<32;i=i+1) begin 135 | reverse[i] = in[31-i]; 136 | end 137 | endfunction 138 | 139 | // Function to apply selected bit operation to a word 140 | function [31:0] bit_op ( 141 | input [31:0] in, 142 | input [1:0] op 143 | ); 144 | 145 | case (op) 146 | 0: bit_op = in; 147 | 1: bit_op = ~in; 148 | 2: bit_op = reverse(in); 149 | 3: bit_op = in; // Reserved 150 | endcase 151 | endfunction 152 | 153 | // Tasks to set registers 154 | task set_x ( 155 | input [31:0] val 156 | ); 157 | begin 158 | setx = 1; 159 | new_val = val; 160 | end 161 | endtask 162 | 163 | task set_y ( 164 | input [31:0] val 165 | ); 166 | begin 167 | sety = 1; 168 | new_val = val; 169 | end 170 | endtask 171 | 172 | task set_exec ( 173 | input [31:0] val 174 | ); 175 | begin 176 | exec = 1; 177 | new_val = val; 178 | end 179 | endtask 180 | 181 | task set_pc ( 182 | input [31:0] val 183 | ); 184 | begin 185 | jmp = 1; 186 | new_val = val; 187 | end 188 | endtask 189 | 190 | task set_isr ( 191 | input [31:0] val 192 | ); 193 | begin 194 | set_shift_in = 1; 195 | new_val = val; 196 | end 197 | endtask 198 | 199 | task set_osr ( 200 | input [31:0] val 201 | ); 202 | begin 203 | set_shift_out = 1; 204 | new_val = val; 205 | end 206 | endtask 207 | 208 | task do_shift_in ( 209 | input [31:0] val 210 | ); 211 | begin 212 | do_in_shift = 1; 213 | new_val = val; 214 | end 215 | endtask 216 | 217 | task pins_set ( 218 | input [31:0] val 219 | ); 220 | begin 221 | set_set_pins = 1; 222 | new_val = val; 223 | end 224 | endtask 225 | 226 | task dirs_set ( 227 | input [31:0] val 228 | ); 229 | begin 230 | set_set_dirs = 1; 231 | new_val = val; 232 | end 233 | endtask 234 | 235 | task pins_out ( 236 | input [31:0] val 237 | ); 238 | begin 239 | set_out_pins = 1; 240 | new_val = val; 241 | end 242 | endtask 243 | 244 | task dirs_out ( 245 | input [31:0] val 246 | ); 247 | begin 248 | set_out_dirs = 1; 249 | new_val = val; 250 | end 251 | endtask 252 | 253 | task do_push(); 254 | begin 255 | push = 1; 256 | dout = in_shift; 257 | end 258 | endtask 259 | 260 | task do_pull (); 261 | begin 262 | pull = 1; 263 | set_shift_out = 1; 264 | new_val = din; 265 | end 266 | endtask 267 | 268 | // Instructions 269 | localparam JMP = 0; 270 | localparam WAIT = 1; 271 | localparam IN = 2; 272 | localparam OUT = 3; 273 | localparam PUSH = 4; 274 | localparam PULL = 4; 275 | localparam MOV = 5; 276 | localparam IRQ = 6; 277 | localparam SET = 7; 278 | 279 | // Count down if delay 280 | always @(posedge clk) begin 281 | if (reset || restart) begin 282 | delay_cnt <= 0; 283 | pin_directions <= 32'h00000000; 284 | output_pins <= 32'h00000000; 285 | end else if (en & penable) begin 286 | exec1 <= exec; // Do execition on next cycle after exec set 287 | exec_instr <= new_val; 288 | if (delaying) delay_cnt <= delay_cnt - 1; 289 | else if (!waiting && !exec && delay > 0) delay_cnt <= delay; 290 | end 291 | end 292 | 293 | integer i; 294 | 295 | always @(posedge clk) begin 296 | if (enabled && !delaying) begin 297 | if (sideset_enabled && !(auto && !waiting)) // TODO Is auto test correct? 298 | for (i=0;i<5;i++) 299 | if (pins_side_count > i) output_pins[pins_side_base+i] <= side_set[i]; 300 | if (set_set_pins) 301 | for (i=0;i<5;i++) 302 | if (pins_set_count > i) output_pins[pins_set_base+i] <= new_val[i]; 303 | if (set_set_dirs) 304 | for (i=0;i<5;i++) 305 | if (pins_set_count > i) pin_directions[pins_set_base+i] <= new_val[i]; 306 | if (set_out_pins) 307 | for (i=0;i<5;i++) 308 | if (pins_out_count > i) output_pins[pins_out_base+i] <= new_val[i]; 309 | if (set_out_dirs) 310 | for (i=0;i<5;i++) 311 | if (pins_out_count > i) pin_directions[pins_out_base+i] <= new_val[i]; 312 | end 313 | end 314 | 315 | // Execute the current instruction 316 | always @* begin 317 | jmp = 0; 318 | pull = 0; 319 | push = 0; 320 | set_shift_in = 0; 321 | set_shift_out = 0; 322 | do_in_shift = 0; 323 | do_out_shift = 0; 324 | decx = 0; 325 | decy = 0; 326 | setx = 0; 327 | sety = 0; 328 | exec = 0; 329 | waiting = 0; 330 | auto = 0; 331 | new_val = 0; 332 | bit_count = 0; 333 | set_set_pins = 0; 334 | set_set_dirs = 0; 335 | set_out_pins = 0; 336 | set_out_dirs = 0; 337 | irq_flags_out = 0; 338 | dout = 0; 339 | if (enabled && !delaying) begin 340 | case (op) 341 | JMP: begin 342 | new_val[4:0] = address; 343 | case (condition) // Condition 344 | 0: jmp = 1; 345 | 1: jmp = (x == 0); 346 | 2: begin jmp = (x != 0); decx = (x != 0); end 347 | 3: jmp = (y == 0); 348 | 4: begin jmp = (y != 0); decy = (y != 0); end 349 | 5: jmp = (x != y); 350 | 6: jmp = jmp_pin; 351 | 7: jmp = (osr_count < osr_threshold); 352 | endcase 353 | end 354 | WAIT: case (source2) // Source 355 | 0: waiting = input_pins[index] != polarity; 356 | 1: waiting = input_pins[pins_in_base + index] != polarity; 357 | 2: waiting = irq_flags_out[irq_index] != polarity; 358 | endcase 359 | IN: if (auto_push && isr_count >= isr_threshold) begin // Auto push 360 | do_push(); 361 | set_isr(0); 362 | waiting = full; 363 | auto = 1; 364 | end else case (source) // Source 365 | 0: do_shift_in(in_pins); 366 | 1: do_shift_in(x); 367 | 2: do_shift_in(y); 368 | 3: do_shift_in(null); 369 | 6: do_shift_in(in_shift); 370 | 7: do_shift_in(out_shift); 371 | endcase 372 | OUT: if (auto_pull && osr_count >= osr_threshold) begin // Auto push 373 | do_pull(); 374 | waiting = empty; 375 | auto = 1; 376 | end else case (destination) // Destination 377 | 0: begin do_out_shift = 1; pins_out(out_shift); end // PINS 378 | 1: begin do_out_shift = 1; set_x(out_shift); end // X 379 | 2: begin do_out_shift = 1; set_y(out_shift); end // Y 380 | 4: begin do_out_shift = 1; dirs_out(out_shift); end // PINDIRS 381 | 5: begin do_out_shift = 1; set_pc(out_shift); ; end // PC 382 | 6: begin do_out_shift = 1; set_isr(out_shift); bit_count = op2; end // ISR 383 | 7: begin do_out_shift = 1; set_exec(out_shift[15:0]); end // EXEC 384 | endcase 385 | PUSH: if (!op1[2]) begin // PUSH TODO No-op when auto-push? 386 | if (!if_full || (isr_count >= isr_threshold)) begin 387 | do_push(); 388 | set_isr(0); 389 | waiting = blocking && full; 390 | end 391 | end else begin // PULL TODO No-op when auto-pull? 392 | if (!if_empty || (osr_count >= osr_threshold)) begin 393 | if (blocking) begin // Blocking 394 | do_pull(); 395 | waiting = empty; 396 | end else begin 397 | if (empty) begin // Copy X to OSR 398 | set_osr(x); 399 | end else begin // Pull value if available 400 | do_pull(); 401 | end 402 | end 403 | end 404 | end 405 | MOV: case (destination) // Destination TODO Status source 406 | 0: case (mov_source) // PINS 407 | 1: pins_out(bit_op(x, mov_op)); // X 408 | 2: pins_out(bit_op(y, mov_op)); // Y 409 | 3: pins_out(bit_op(null, mov_op)); // NULL 410 | 6: pins_out(bit_op(in_shift, mov_op)); // ISR 411 | 7: pins_out(bit_op(out_shift, mov_op)); // OSR 412 | endcase 413 | 1: case (mov_source) // X 414 | 0: set_x(bit_op(in_pins, mov_op)); // PINS 415 | 2: set_x(bit_op(y, mov_op)); // Y 416 | 3: set_x(bit_op(null, mov_op)); // NULL 417 | 6: set_x(bit_op(in_shift, mov_op)); // ISR 418 | 7: set_x(bit_op(out_shift, mov_op)); // OSR 419 | endcase 420 | 2: case (mov_source) // Y 421 | 0: set_y(bit_op(in_pins, mov_op)); // PINS 422 | 1: set_y(bit_op(x, mov_op)); // X 423 | 3: set_y(bit_op(null, mov_op)); // NULL 424 | 6: set_y(bit_op(in_shift, mov_op)); // ISR 425 | 7: set_y(bit_op(out_shift, mov_op)); // OSR 426 | endcase 427 | 4: case (mov_source) // EXEC 428 | 0: set_exec(bit_op(in_pins, mov_op)); // PINS 429 | 1: set_exec(bit_op(x, mov_op)); // X 430 | 2: set_exec(bit_op(y, mov_op)); // Y 431 | 3: set_exec(bit_op(null, mov_op)); // NULL 432 | 6: set_exec(bit_op(in_shift, mov_op)); // ISR 433 | 7: set_exec(bit_op(out_shift, mov_op)); // OSR 434 | endcase 435 | 5: case (mov_source) // PC 436 | 0: set_pc(bit_op(in_pins, mov_op)); // PINS 437 | 1: set_pc(bit_op(x, mov_op)); // X 438 | 2: set_pc(bit_op(y, mov_op)); // Y 439 | 3: set_pc(bit_op(null, mov_op)); // NULL 440 | 6: set_pc(bit_op(in_shift, mov_op)); // ISR 441 | 7: set_pc(bit_op(out_shift, mov_op)); // OSR 442 | endcase 443 | 6: case (mov_source) // ISR 444 | 0: set_isr(bit_op(in_pins, mov_op)); // PINS 445 | 1: set_isr(bit_op(x, mov_op)); // X 446 | 2: set_isr(bit_op(y, mov_op)); // Y 447 | 3: set_isr(bit_op(null, mov_op)); // NULL 448 | 6: set_isr(bit_op(in_shift, mov_op)); // ISR 449 | 7: set_isr(bit_op(out_shift, mov_op)); // OSR 450 | endcase 451 | 7: case (mov_source) // OSR 452 | 0: set_osr(bit_op(in_pins, mov_op)); // PINS 453 | 1: set_osr(bit_op(x, mov_op)); // X 454 | 2: set_osr(bit_op(y, mov_op)); // Y 455 | 3: set_osr(bit_op(null, mov_op)); // NULL 456 | 6: set_osr(bit_op(in_shift, mov_op)); // ISR 457 | 7: set_osr(bit_op(out_shift, mov_op)); // OSR 458 | endcase 459 | endcase 460 | IRQ: begin 461 | if (op1[1]) irq_flags_out[irq_index] = 0; // CLEAR 462 | else begin // SET 463 | irq_flags_out[irq_index] = 1; 464 | waiting = blocking && irq_flags_in[irq_index] != 0; // If wait set, wait for irq cleared 465 | end 466 | end 467 | SET: case (destination) // Destination 468 | 0: pins_set(data); // PINS 469 | 1: set_x({27'b0, data}); // X 470 | 2: set_y({27'b0, data}); // Y 471 | 4: dirs_set(data); // PINDIRS 472 | endcase 473 | endcase 474 | end 475 | end 476 | 477 | // Clock divider 478 | divider clk_divider ( 479 | .clk(clk), 480 | .reset(reset | restart), 481 | .div(div), 482 | .use_divider(use_divider), 483 | .penable(penable), 484 | .pclk(pclk) 485 | ); 486 | 487 | // Instruction decoder 488 | decoder decode ( 489 | .instr(exec1 ? exec_instr : instr), 490 | .sideset_bits(pins_side_count), 491 | .sideset_enable_bit(sideset_enable_bit), 492 | .sideset_enabled(sideset_enabled), 493 | .op(op), 494 | .op1(op1), 495 | .op2(op2), 496 | .delay(delay), 497 | .side_set(side_set) 498 | ); 499 | 500 | // Synchronous modules 501 | // PC 502 | pc pc_reg ( 503 | .clk(clk), 504 | .penable(en & penable), 505 | .reset(reset | restart), 506 | .din(new_val[4:0]), 507 | .jmp(jmp), 508 | .stalled(waiting || auto || imm || exec1 || delaying), 509 | .pend(pend), 510 | .wrap_target(wrap_target), 511 | .dout(pc) 512 | ); 513 | 514 | // X 515 | scratch scratch_x ( 516 | .clk(clk), 517 | .penable(enabled), 518 | .reset(reset | restart), 519 | .stalled(delaying), 520 | .din(new_val), 521 | .set(setx), 522 | .dec(decx), 523 | .dout(x) 524 | ); 525 | 526 | // Y 527 | scratch scratch_y ( 528 | .clk(clk), 529 | .penable(enabled), 530 | .reset(reset | restart), 531 | .stalled(delaying), 532 | .din(new_val), 533 | .set(sety), 534 | .dec(decy), 535 | .dout(y) 536 | ); 537 | 538 | // ISR 539 | isr shift_in ( 540 | .clk(clk), 541 | .penable(enabled), 542 | .reset(reset | restart), 543 | .stalled(waiting ||delaying), 544 | .dir(in_shift_dir), 545 | .shift(op2), 546 | .set(set_shift_in), 547 | .do_shift(do_in_shift), 548 | .din(new_val), 549 | .dout(in_shift), 550 | .bit_count(bit_count), 551 | .shift_count(isr_count) 552 | ); 553 | 554 | // OSR 555 | osr shift_out ( 556 | .clk(clk), 557 | .penable(enabled), 558 | .reset(reset | restart), 559 | .stalled(waiting || delaying), 560 | .dir(out_shift_dir), 561 | .shift(op2), 562 | .set(set_shift_out), 563 | .do_shift(do_out_shift), 564 | .din(new_val), 565 | .dout(out_shift), 566 | .shift_count(osr_count) 567 | ); 568 | 569 | endmodule 570 | -------------------------------------------------------------------------------- /src/osr.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module osr ( 6 | input clk, 7 | input penable, 8 | input reset, 9 | input stalled, 10 | input [31:0] din, 11 | input [4:0] shift, 12 | input dir, // 0 - left, 1 - right 13 | input set, 14 | input do_shift, 15 | output [31:0] dout, 16 | output [5:0] shift_count 17 | ); 18 | 19 | reg [31:0] shift_reg; 20 | reg [5:0] count; 21 | 22 | // A shift value of 0 means shift 32 23 | wire [5:0] shift_val = shift == 0 ? 32 : shift; 24 | // Calculate the 64-bit value of the shift register after a shift 25 | wire [63:0] shift64 = dir ? {shift_reg, 32'b0} >> shift_val : {32'b0, shift_reg} << shift_val; 26 | // Calculate the right-aligned shifted out value 27 | wire [31:0] shift_out = dir ? (shift64[31:0] >> (32 - shift_val)) : shift64[63:32]; 28 | // Calculate the new shift register value after a shift 29 | wire [31:0] new_shift = dir ? shift64[63:32] : shift64[31:0]; 30 | 31 | always @(posedge clk) begin 32 | if (reset) begin 33 | shift_reg <= 0; 34 | count <= 32; // Empty (read to trigger auto-pull) 35 | end else if (penable && !stalled) begin 36 | if (set) begin 37 | shift_reg <= din; 38 | count <= 0; 39 | end else if (do_shift) begin 40 | shift_reg <= new_shift; 41 | count <= count + shift_val > 32 ? 32 : count + shift_val; 42 | end 43 | end 44 | end 45 | 46 | // The output value is the amount shifted out if do_shift ia active otherwise the current shift register 47 | assign dout = do_shift ? shift_out : shift_reg; 48 | assign shift_count = count; 49 | 50 | endmodule 51 | -------------------------------------------------------------------------------- /src/pc.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module pc ( 6 | input clk, 7 | input penable, 8 | input reset, 9 | input [4:0] din, 10 | input jmp, 11 | input [4:0] pend, 12 | input stalled, 13 | input [4:0] wrap_target, 14 | output [4:0] dout 15 | ); 16 | 17 | reg [4:0] index = 0; 18 | 19 | assign dout = (penable && !stalled) ? (jmp ? din : index == pend ? wrap_target : index + 1) : index; 20 | 21 | always @(posedge clk) begin 22 | if (reset) 23 | index <= 0; 24 | else if (penable && !stalled) begin 25 | if (jmp) 26 | index <= din; 27 | else 28 | index <= index == pend ? wrap_target : index + 1; 29 | end 30 | end 31 | 32 | endmodule 33 | 34 | -------------------------------------------------------------------------------- /src/pio.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module pio #( 6 | parameter NUM_MACHINES = 4 7 | ) ( 8 | input clk, 9 | input reset, 10 | input [1:0] mindex, 11 | input [31:0] din, 12 | input [4:0] index, 13 | input [3:0] action, 14 | input [31:0] gpio_in, 15 | output reg [31:0] gpio_out, 16 | output reg [31:0] gpio_dir, 17 | output reg [31:0] dout, 18 | output irq0, 19 | output irq1, 20 | output [3:0] tx_full, 21 | output [3:0] rx_empty, 22 | output [3:0] pclk 23 | ); 24 | 25 | // Shared instructions memory 26 | reg [15:0] instr [0:31]; 27 | 28 | // Configuration 29 | reg [NUM_MACHINES-1:0] en; 30 | reg [NUM_MACHINES-1:0] auto_pull; 31 | reg [NUM_MACHINES-1:0] auto_push; 32 | reg [NUM_MACHINES-1:0] sideset_enable_bit; 33 | reg [NUM_MACHINES-1:0] in_shift_dir; 34 | reg [NUM_MACHINES-1:0] out_shift_dir; 35 | reg [NUM_MACHINES-1:0] status_sel; 36 | reg [NUM_MACHINES-1:0] out_sticky; 37 | reg [NUM_MACHINES-1:0] inline_out_en; 38 | reg [NUM_MACHINES-1:0] side_pindir; 39 | reg [NUM_MACHINES-1:0] exec_stalled; 40 | reg [NUM_MACHINES-1:0] use_divider; 41 | 42 | // Control 43 | reg [NUM_MACHINES-1:0] imm; 44 | reg [NUM_MACHINES-1:0] push; 45 | reg [NUM_MACHINES-1:0] pull; 46 | reg [NUM_MACHINES-1:0] restart; 47 | reg [NUM_MACHINES-1:0] clkdiv_restart; 48 | 49 | // Configuration 50 | reg [4:0] pend [0:NUM_MACHINES-1]; 51 | reg [4:0] wrap_target [0:NUM_MACHINES-1]; 52 | reg [23:0] div [0:NUM_MACHINES-1]; 53 | reg [4:0] pins_in_base [0:NUM_MACHINES-1]; 54 | reg [4:0] pins_out_base [0:NUM_MACHINES-1]; 55 | reg [4:0] pins_set_base [0:NUM_MACHINES-1]; 56 | reg [4:0] pins_side_base [0:NUM_MACHINES-1]; 57 | reg [5:0] pins_out_count [0:NUM_MACHINES-1]; 58 | reg [2:0] pins_set_count [0:NUM_MACHINES-1]; 59 | reg [2:0] pins_side_count [0:NUM_MACHINES-1]; 60 | reg [4:0] isr_threshold [0:NUM_MACHINES-1]; 61 | reg [4:0] osr_threshold [0:NUM_MACHINES-1]; 62 | reg [3:0] status_n [0:NUM_MACHINES-1]; 63 | reg [4:0] out_en_sel [0:NUM_MACHINES-1]; 64 | reg [4:0] jmp_pin [0:NUM_MACHINES-1]; 65 | 66 | (* mem2reg *) reg [15:0] curr_instr [0:NUM_MACHINES-1]; 67 | 68 | // Output from machines and fifos 69 | wire [31:0] output_pins [0:NUM_MACHINES-1]; 70 | reg [31:0] output_pins_prev [0:NUM_MACHINES-1]; 71 | wire [31:0] pin_directions [0:NUM_MACHINES-1]; 72 | reg [31:0] pin_directions_prev [0:NUM_MACHINES-1]; 73 | wire [4:0] pc [0:NUM_MACHINES-1]; 74 | wire [31:0] mdin [0:NUM_MACHINES-1]; 75 | wire [31:0] mdout [0:NUM_MACHINES-1]; 76 | wire [31:0] pdout [0:NUM_MACHINES-1]; 77 | wire [7:0] irq_flags_out [0:NUM_MACHINES-1]; 78 | wire [2:0] rx_level [0:NUM_MACHINES-1]; 79 | wire [2:0] tx_level [0:NUM_MACHINES-1]; 80 | 81 | wire [NUM_MACHINES-1:0] mempty; 82 | wire [NUM_MACHINES-1:0] mfull; 83 | wire [NUM_MACHINES-1:0] mpush; 84 | wire [NUM_MACHINES-1:0] mpull; 85 | 86 | integer i; 87 | integer gpio_idx; 88 | 89 | // Synchronous fetch of current instruction for each machine 90 | always @(posedge clk) begin 91 | for(i=0;i= 24'h200; // Use divider if clock divider is 2 or more 200 | end 201 | IMM : imm[mindex] <= 1; // Immediate instruction 202 | SHIFT: begin 203 | auto_push[mindex] <= din[16]; // SHIFT_CTRL 204 | auto_pull[mindex] <= din[17]; 205 | in_shift_dir[mindex] <= din[18]; 206 | out_shift_dir[mindex] <= din[19]; 207 | isr_threshold[mindex] <= din[24:20]; 208 | osr_threshold[mindex] <= din[29:25]; 209 | end 210 | NONE : dout <= 32'h01000000; // Hardware version number 211 | endcase 212 | end 213 | end 214 | 215 | // Generate the machines and associated TX and RX fifos 216 | generate 217 | genvar j; 218 | 219 | for(j=0;j 0 ? sideset_enable_bit[j] : 1'b0), 231 | .in_shift_dir(in_shift_dir[j]), 232 | .out_shift_dir(out_shift_dir[j]), 233 | .div(div[j]), 234 | .use_divider(use_divider[j]), 235 | .instr(imm[j] ? din[15:0] : curr_instr[j]), 236 | .imm(imm[j]), 237 | .pend(pend[j]), 238 | .wrap_target(wrap_target[j]), 239 | .pins_out_base(pins_out_base[j]), 240 | .pins_out_count(pins_out_count[j]), 241 | .pins_set_base(pins_set_base[j]), 242 | .pins_set_count(pins_set_count[j]), 243 | .pins_in_base(pins_in_base[j]), 244 | .pins_side_base(pins_side_base[j]), 245 | .pins_side_count(pins_side_count[j]), 246 | .auto_pull(auto_pull[j]), 247 | .auto_push(auto_push[j]), 248 | .isr_threshold(isr_threshold[j]), 249 | .osr_threshold(osr_threshold[j]), 250 | .irq_flags_in(8'h0), 251 | .irq_flags_out(irq_flags_out[j]), 252 | .pc(pc[j]), 253 | .din(mdin[j]), 254 | .dout(mdout[j]), 255 | .pull(mpull[j]), 256 | .push(mpush[j]), 257 | .pclk(pclk[j]), 258 | .empty(mempty[j]), 259 | .full(mfull[j]) 260 | ); 261 | 262 | fifo fifo_tx ( 263 | .clk(clk), 264 | .reset(reset), 265 | .push(push[j]), 266 | .pull(mpull[j]), 267 | .din(din), 268 | .dout(mdin[j]), 269 | .empty(mempty[j]), 270 | .full(tx_full[j]), 271 | .level(tx_level[j]) 272 | ); 273 | 274 | fifo fifo_rx ( 275 | .clk(clk), 276 | .reset(reset), 277 | .push(mpush[j]), 278 | .pull(pull[j]), 279 | .din(mdout[j]), 280 | .dout(pdout[j]), 281 | .full(mfull[j]), 282 | .empty(rx_empty[j]), 283 | .level(rx_level[j]) 284 | ); 285 | end 286 | endgenerate 287 | 288 | endmodule 289 | -------------------------------------------------------------------------------- /src/scratch.v: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Lawrie Griffiths 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | `default_nettype none 5 | module scratch ( 6 | input clk, 7 | input penable, 8 | input reset, 9 | input stalled, 10 | input [31:0] din, 11 | input set, 12 | input dec, 13 | output [31:0] dout 14 | ); 15 | 16 | reg [31:0] val; 17 | 18 | assign dout = val; 19 | 20 | always @(posedge clk) begin 21 | if (reset) 22 | val <= 0; 23 | else if (penable && !stalled) begin 24 | if (set) 25 | val <= din; 26 | else if (dec) 27 | val <= val - 1; 28 | end 29 | end 30 | 31 | endmodule 32 | 33 | -------------------------------------------------------------------------------- /src/top/blink.mem: -------------------------------------------------------------------------------- 1 | e081 2 | ff01 3 | bf42 4 | bf42 5 | bf42 6 | bf42 7 | fe00 8 | bf42 9 | bf42 10 | bf42 11 | bf42 12 | 0001 13 | -------------------------------------------------------------------------------- /src/top/blink.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("blink.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 4; // Config length 43 | initial $readmemh("blink_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | always @(posedge clk_25mhz) begin 51 | if (reset) begin 52 | din <= 0; 53 | action <= 0; 54 | index <= 0; 55 | mindex <= 0; 56 | gpio_in <= 0; 57 | state <= 0; 58 | cindex <= 0; 59 | pindex <= 0; 60 | end else begin 61 | case (state) 62 | 0: begin // Send program to pio 63 | action <= 1; 64 | din <= program[pindex]; 65 | pindex <= pindex + 1; 66 | index <= pindex; 67 | if (pindex == 31) 68 | state <= 1; 69 | end 70 | 1: begin // Do configuration 71 | cindex <= cindex + 1; 72 | if (cindex == clen) begin 73 | state <= 2; 74 | action <= 0; 75 | end else begin 76 | action <= conf[cindex][35:32]; 77 | din <= conf[cindex][31:0]; 78 | end 79 | end 80 | 2: begin // Run state 81 | end 82 | endcase 83 | end 84 | end 85 | 86 | // PIO instance 1 87 | pio pio_1 ( 88 | .clk(clk_25mhz), 89 | .reset(reset), 90 | .mindex(mindex), 91 | .din(din), 92 | .index(index), 93 | .action(action), 94 | .dout(dout), 95 | .gpio_in(gpio_in), 96 | .gpio_out(gpio_out), 97 | .gpio_dir(gpio_dir), 98 | .irq0(irq0), 99 | .irq1(irq1), 100 | .tx_full(tx_full), 101 | .rx_empty(rx_empty) 102 | ); 103 | 104 | // Led and gpio outpuy 105 | assign led = {reset, gpio_out[0]}; 106 | assign gn[0] = gpio_out[0]; 107 | assign tx = 0; 108 | 109 | endmodule 110 | 111 | -------------------------------------------------------------------------------- /src/top/blink_conf.mem: -------------------------------------------------------------------------------- 1 | 20000c000 // Set wrap 2 | 700ffff00 // Set divider 3 | 504000000 // Set pin groups, SET pin 0 4 | 600000001 // Enable machine 1 5 | 6 | -------------------------------------------------------------------------------- /src/top/copy_conf.mem: -------------------------------------------------------------------------------- 1 | 200001000 // Set wrap 2 | 700000100 // Set divider 3 | 500100000 // Set pin groups, OUT pin 0 4 | A00080000 // Shift out direction right 5 | 600000001 // Enable machine 1 6 | 7 | -------------------------------------------------------------------------------- /src/top/copybit.mem: -------------------------------------------------------------------------------- 1 | 80a0 2 | 6001 3 | -------------------------------------------------------------------------------- /src/top/copybit.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("copybit.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 5; // Config length 43 | initial $readmemh("copy_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | reg [21:0] delay_cnt; 51 | reg val; 52 | 53 | always @(posedge clk_25mhz) begin 54 | if (reset) begin 55 | din <= 0; 56 | action <= 0; 57 | index <= 0; 58 | mindex <= 0; 59 | gpio_in <= 0; 60 | state <= 0; 61 | cindex <= 0; 62 | pindex <= 0; 63 | val <= 0; 64 | end else begin 65 | case (state) 66 | 0: begin // Send program to pio 67 | action <= 1; 68 | din <= program[pindex]; 69 | pindex <= pindex + 1; 70 | index <= pindex; 71 | if (pindex == 31) 72 | state <= 1; 73 | end 74 | 1: begin // Do configuration 75 | cindex <= cindex + 1; 76 | if (cindex == clen) begin 77 | state <= 2; 78 | action <= 0; 79 | end else begin 80 | action <= conf[cindex][35:32]; 81 | din <= conf[cindex][31:0]; 82 | end 83 | end 84 | 2: begin // Run state 85 | delay_cnt <= delay_cnt + 1; 86 | if (delay_cnt == 0 && !tx_full[0]) begin 87 | action <= 4; // PUSH 88 | din = val; 89 | val = ~val; 90 | end else if (delay_cnt == 1) begin 91 | action <= 0; 92 | end 93 | end 94 | endcase 95 | end 96 | end 97 | 98 | // PIO instance 1 99 | pio pio_1 ( 100 | .clk(clk_25mhz), 101 | .reset(reset), 102 | .mindex(mindex), 103 | .din(din), 104 | .index(index), 105 | .action(action), 106 | .dout(dout), 107 | .gpio_in(gpio_in), 108 | .gpio_out(gpio_out), 109 | .gpio_dir(gpio_dir), 110 | .irq0(irq0), 111 | .irq1(irq1), 112 | .tx_full(tx_full), 113 | .rx_empty(rx_empty) 114 | ); 115 | 116 | // Led and gpio outpuy 117 | assign led = ~gpio_out[0]; 118 | 119 | endmodule 120 | 121 | -------------------------------------------------------------------------------- /src/top/echo.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | 10 | input rx, 11 | output tx 12 | ); 13 | 14 | // PIO registers and wires 15 | reg [31:0] din; // Data sent to PIO 16 | reg [4:0] index; // Instruction index 17 | reg [3:0] action; // Action to be done by PIO 18 | reg [1:0] mindex; // Machine index 19 | wire [31:0] gpio_in; // Input pins to PIO 20 | 21 | wire [31:0] gpio_out; // Output pins from PIO 22 | wire [31:0] gpio_dir; // Pin directions 23 | wire [31:0] dout; // Output from PIO 24 | wire irq0, irq1; // IRQ flags from PIO 25 | wire [3:0] tx_full; // Set when TX fifo is full 26 | wire [3:0] rx_empty; // Set when RX fifo is empty 27 | 28 | wire [4:0] offset = 4; 29 | 30 | // Power-on reset 31 | reg [15:0] pwr_up_reset_counter = 0; 32 | wire pwr_up_reset_n = &pwr_up_reset_counter; 33 | wire n_reset = pwr_up_reset_n & btn[0]; 34 | wire reset = ~n_reset; 35 | 36 | always @(posedge clk_25mhz) begin 37 | if (!pwr_up_reset_n) 38 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 39 | end 40 | 41 | // Configuration of state machines and program instructions 42 | reg [15:0] program1 [0:31]; 43 | initial $readmemh("uart_rx.mem", program1); 44 | wire [4:0] plen1 = 4; 45 | 46 | reg [15:0] program2 [0:31]; 47 | initial $readmemh("uart_tx.mem", program2); 48 | wire [4:0] plen2 = 4; 49 | 50 | reg [35:0] conf1 [0:31]; 51 | initial $readmemh("rx_conf.mem", conf1); 52 | wire [4:0] clen1 = 5; // Config 1 length 53 | 54 | reg [35:0] conf2 [0:31]; 55 | initial $readmemh("tx_conf2.mem", conf2); 56 | wire [4:0] clen2 = 6; // Config 2 length 57 | 58 | // State machine to send program to PIO and configure PIO state machines 59 | reg [2:0] state; 60 | reg [4:0] cindex; 61 | reg [4:0] pindex; 62 | 63 | reg [2:0] delay_cnt; 64 | reg rx_ready; 65 | 66 | reg [7:0] data; 67 | 68 | always @(posedge clk_25mhz) begin 69 | if (reset) begin 70 | din <= 0; 71 | action <= 0; 72 | index <= 0; 73 | mindex <= 0; 74 | state <= 0; 75 | cindex <= 0; 76 | pindex <= 0; 77 | end else begin 78 | case (state) 79 | 0: begin // Send program 1 to pio 80 | if (pindex < plen1) begin 81 | action <= 1; 82 | din <= program1[pindex]; 83 | pindex <= pindex + 1; 84 | index <= pindex; 85 | end else begin 86 | state <= 1; 87 | pindex <= 0; 88 | action <= 0; 89 | end 90 | end 91 | 1: begin // Send program 2 to pio 92 | if (pindex < plen2) begin 93 | action <= 1; 94 | din <= program2[pindex][15:13] == 0 ? program2[pindex] + offset : program2[pindex]; 95 | pindex <= pindex + 1; 96 | index <= pindex + offset; 97 | end else begin 98 | state <= 2; 99 | action <= 0; 100 | mindex <= 0; 101 | end 102 | end 103 | 2: begin // Configure machine 1 104 | cindex <= cindex + 1; 105 | if (cindex == clen1) begin 106 | state <= 3; 107 | action <= 0; 108 | mindex <= 1; 109 | cindex <= 0; 110 | end else begin 111 | action <= conf1[cindex][35:32]; 112 | din <= conf1[cindex][31:0]; 113 | end 114 | end 115 | 3: begin // Configure machine 2 116 | cindex <= cindex + 1; 117 | if (cindex == clen2) begin 118 | state <= 4; 119 | action <= 0; 120 | end else begin 121 | action <= conf2[cindex][35:32]; 122 | din <= conf2[cindex][31:0]; 123 | end 124 | end 125 | 4: begin // Run state 126 | action <= 0; 127 | delay_cnt <= delay_cnt + 1; 128 | rx_ready <= 0; 129 | if (!rx_empty[0] && &delay_cnt) begin 130 | mindex <= 0; 131 | action <= 3; // Pull 132 | rx_ready <= 1; 133 | end 134 | if (rx_ready) begin 135 | mindex <= 1; 136 | din <= dout[31:24]; 137 | data <= dout[31:24]; 138 | action <= 4; // Push 139 | end 140 | end 141 | endcase 142 | end 143 | end 144 | 145 | // PIO instance 1 146 | pio pio_1 ( 147 | .clk(clk_25mhz), 148 | .reset(reset), 149 | .mindex(mindex), 150 | .din(din), 151 | .index(index), 152 | .action(action), 153 | .dout(dout), 154 | .gpio_in(gpio_in), 155 | .gpio_out(gpio_out), 156 | .gpio_dir(gpio_dir), 157 | .irq0(irq0), 158 | .irq1(irq1), 159 | .tx_full(tx_full), 160 | .rx_empty(rx_empty) 161 | ); 162 | 163 | // Led and gpio output 164 | `ifdef blackicemx 165 | assign led = ~{rx_ready, rx_empty[0]}; 166 | `else 167 | assign led = data; 168 | `endif 169 | 170 | assign tx = gpio_out[0]; 171 | assign gpio_in[0] = rx; 172 | 173 | endmodule 174 | 175 | -------------------------------------------------------------------------------- /src/top/exec.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("blink.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 3; // Config length 43 | initial $readmemh("exec_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | reg [21:0] delay_cnt; 51 | reg pin_val; 52 | 53 | always @(posedge clk_25mhz) begin 54 | if (reset) begin 55 | din <= 0; 56 | action <= 0; 57 | index <= 0; 58 | mindex <= 0; 59 | gpio_in <= 0; 60 | state <= 0; 61 | cindex <= 0; 62 | pindex <= 0; 63 | end else begin 64 | case (state) 65 | 0: begin // Send program to pio 66 | action <= 1; 67 | din <= program[pindex]; 68 | pindex <= pindex + 1; 69 | index <= pindex; 70 | if (pindex == 31) 71 | state <= 1; 72 | end 73 | 1: begin // Do configuration 74 | cindex <= cindex + 1; 75 | if (cindex == clen) begin 76 | state <= 2; 77 | action <= 0; 78 | end else begin 79 | action <= conf[cindex][35:32]; 80 | din <= conf[cindex][31:0]; 81 | end 82 | end 83 | 2: begin // Run state 84 | delay_cnt <= delay_cnt + 1; 85 | if (delay_cnt == 0) begin 86 | action <= 9; 87 | din <= 16'b111_00000_000_00000 + pin_val; // Execute set pin 88 | pin_val= ~pin_val; 89 | end else if (delay_cnt == 1) begin 90 | action <= 0; 91 | end 92 | end 93 | endcase 94 | end 95 | end 96 | 97 | // PIO instance 1 98 | pio pio_1 ( 99 | .clk(clk_25mhz), 100 | .reset(reset), 101 | .mindex(mindex), 102 | .din(din), 103 | .index(index), 104 | .action(action), 105 | .dout(dout), 106 | .gpio_in(gpio_in), 107 | .gpio_out(gpio_out), 108 | .gpio_dir(gpio_dir), 109 | .irq0(irq0), 110 | .irq1(irq1), 111 | .tx_full(tx_full), 112 | .rx_empty(rx_empty) 113 | ); 114 | 115 | // Led and gpio outpuy 116 | assign led = {reset, gpio_out[0]}; 117 | assign gn[0] = gpio_out[0]; 118 | assign tx = 0; 119 | 120 | endmodule 121 | 122 | -------------------------------------------------------------------------------- /src/top/exec_conf.mem: -------------------------------------------------------------------------------- 1 | 20000c000 // Set wrap 2 | 700ffff00 // Set divider 3 | 504000000 // Set pin groups, OUT pin 0 4 | 5 | -------------------------------------------------------------------------------- /src/top/guitar.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lawrie/fpga_pio/f38be97cdfa86d4551c96bb98599e3443000628b/src/top/guitar.wav -------------------------------------------------------------------------------- /src/top/hello.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("uart_tx.mem", program); 40 | 41 | wire [103:0] hello = "Hello World!\n"; 42 | 43 | reg [35:0] conf [0:31]; 44 | wire [5:0] clen = 5; // Config length 45 | initial $readmemh("hello_conf.mem", conf); 46 | 47 | // State machine to send program to PIO and configure PIO state machines 48 | reg [1:0] state; 49 | reg [4:0] cindex; 50 | reg [4:0] pindex; 51 | 52 | reg [11:0] delay_cnt; 53 | reg [3:0] cp; 54 | reg [2:0] stalled; 55 | 56 | always @(posedge clk_25mhz) begin 57 | if (reset) begin 58 | din <= 0; 59 | action <= 0; 60 | index <= 0; 61 | mindex <= 0; 62 | gpio_in <= 0; 63 | state <= 0; 64 | cindex <= 0; 65 | pindex <= 0; 66 | stalled <= 0; 67 | end else begin 68 | case (state) 69 | 0: begin // Send program to pio 70 | action <= 1; 71 | din <= program[pindex]; 72 | pindex <= pindex + 1; 73 | index <= pindex; 74 | if (pindex == 31) 75 | state <= 1; 76 | end 77 | 1: begin // Do configuration 78 | cindex <= cindex + 1; 79 | if (cindex == clen) begin 80 | state <= 2; 81 | action <= 0; 82 | end else begin 83 | action <= conf[cindex][35:32]; 84 | din <= conf[cindex][31:0]; 85 | end 86 | end 87 | 2: begin // Run state 88 | if (tx_full[0]) stalled <= stalled + 1; 89 | delay_cnt <= delay_cnt + 1; 90 | if (delay_cnt == 0 && !tx_full[0]) begin 91 | action <= 4; // PUSH 92 | cp <= cp + 1; 93 | if (cp == 12) 94 | cp <= 0; 95 | din <= hello[((12 - cp) * 8) + 7 -: 8]; 96 | end else if (delay_cnt == 1) begin 97 | action <= 0; 98 | end 99 | end 100 | endcase 101 | end 102 | end 103 | 104 | // PIO instance 1 105 | pio pio_1 ( 106 | .clk(clk_25mhz), 107 | .reset(reset), 108 | .mindex(mindex), 109 | .din(din), 110 | .index(index), 111 | .action(action), 112 | .dout(dout), 113 | .gpio_in(gpio_in), 114 | .gpio_out(gpio_out), 115 | .gpio_dir(gpio_dir), 116 | .irq0(irq0), 117 | .irq1(irq1), 118 | .tx_full(tx_full), 119 | .rx_empty(rx_empty) 120 | ); 121 | 122 | // Led and gpio outpuy 123 | assign led = ~stalled; 124 | assign tx = gpio_out[0]; 125 | 126 | endmodule 127 | 128 | -------------------------------------------------------------------------------- /src/top/hello_conf.mem: -------------------------------------------------------------------------------- 1 | 240003000 // Set wrap and sideset enable bit 2 | 700001b00 // Set divider 27 3 | 540100000 // Set pin groups side and out, sideset enable bit 4 | A00080000 // Shift control, shift out right 5 | 600000001 // Enable machine 1 6 | 7 | -------------------------------------------------------------------------------- /src/top/i2s.mem: -------------------------------------------------------------------------------- 1 | 9880 2 | b827 3 | f94e 4 | 7301 5 | 1b83 6 | 6301 7 | eb4e 8 | 6301 9 | 0b87 10 | 7301 11 | -------------------------------------------------------------------------------- /src/top/i2s.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("i2s.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 5; // Config length 43 | initial $readmemh("i2s_conf.mem", conf); 44 | 45 | reg [14:0] muindex; 46 | reg [31:0] music [0:32767]; 47 | initial $readmemh("music.mem", music); 48 | 49 | // State machine to send program to PIO and configure PIO state machines 50 | reg [1:0] state; 51 | reg [4:0] cindex; 52 | reg [4:0] pindex; 53 | 54 | reg [2:0] delay_cnt; 55 | 56 | wire [31:0] sample = music[muindex]; 57 | 58 | always @(posedge clk_25mhz) begin 59 | if (reset) begin 60 | din <= 0; 61 | action <= 0; 62 | index <= 0; 63 | mindex <= 0; 64 | gpio_in <= 0; 65 | state <= 0; 66 | cindex <= 0; 67 | pindex <= 0; 68 | end else begin 69 | case (state) 70 | 0: begin // Send program to pio 71 | action <= 1; 72 | din <= program[pindex]; 73 | pindex <= pindex + 1; 74 | index <= pindex; 75 | if (pindex == 31) 76 | state <= 1; 77 | end 78 | 1: begin // Do configuration 79 | cindex <= cindex + 1; 80 | if (cindex == clen) begin 81 | state <= 2; 82 | action <= 0; 83 | end else begin 84 | action <= conf[cindex][35:32]; 85 | din <= conf[cindex][31:0]; 86 | end 87 | end 88 | 2: begin // Run state 89 | action <= 0; 90 | delay_cnt <= delay_cnt + 1; 91 | if (!tx_full[0] && delay_cnt == 0) begin 92 | action <= 4; // PUSH 93 | din <= {sample[23:16], sample[31:24], sample[7:0], sample[15:8]}; 94 | muindex <= muindex + 1; 95 | end 96 | end 97 | endcase 98 | end 99 | end 100 | 101 | wire [3:0] pclk; 102 | 103 | // PIO instance 1 104 | pio pio_1 ( 105 | .clk(clk_25mhz), 106 | .reset(reset), 107 | .mindex(mindex), 108 | .din(din), 109 | .index(index), 110 | .action(action), 111 | .dout(dout), 112 | .gpio_in(gpio_in), 113 | .gpio_out(gpio_out), 114 | .gpio_dir(gpio_dir), 115 | .irq0(irq0), 116 | .irq1(irq1), 117 | .pclk(pclk), 118 | .tx_full(tx_full), 119 | .rx_empty(rx_empty) 120 | ); 121 | 122 | // Led and gpio output 123 | assign led = ~0; 124 | assign gn[3:0] = {pclk[0], gpio_out[2:0]}; 125 | 126 | endmodule 127 | 128 | -------------------------------------------------------------------------------- /src/top/i2s_conf.mem: -------------------------------------------------------------------------------- 1 | 200009000 // Set wrap 2 | 700000237 // Set divider 3 | 540100400 // OUT pin 0 and sideset pins 1 and 2 4 | A00000000 // Shift control 5 | 600000001 // Enable machine 1 6 | 7 | -------------------------------------------------------------------------------- /src/top/pwm.mem: -------------------------------------------------------------------------------- 1 | 9080 2 | a027 3 | a046 4 | 00a5 5 | 1806 6 | a042 7 | 0083 8 | -------------------------------------------------------------------------------- /src/top/pwm.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("pwm.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 10; // Config length 43 | initial $readmemh("pwm_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | reg [26:0] delay_cnt; 51 | reg [3:0] val; 52 | 53 | wire [3:0] val_bits = delay_cnt[26:23]; 54 | 55 | always @(posedge clk_25mhz) begin 56 | if (reset) begin 57 | din <= 0; 58 | action <= 0; 59 | index <= 0; 60 | mindex <= 0; 61 | gpio_in <= 0; 62 | state <= 0; 63 | cindex <= 0; 64 | pindex <= 0; 65 | end else begin 66 | case (state) 67 | 0: begin // Send program to pio 68 | action <= 1; 69 | din <= program[pindex]; 70 | pindex <= pindex + 1; 71 | index <= pindex; 72 | if (pindex == 31) 73 | state <= 1; 74 | end 75 | 1: begin // Do configuration 76 | cindex <= cindex + 1; 77 | if (cindex == clen) begin 78 | state <= 2; 79 | action <= 0; 80 | end else begin 81 | action <= conf[cindex][35:32]; 82 | din <= conf[cindex][31:0]; 83 | end 84 | end 85 | 2: begin // Run state 86 | delay_cnt <= delay_cnt + 1; 87 | val <= val_bits; 88 | action <= 0; 89 | // Send new value when top 4 bits change 90 | if (val_bits != val) begin 91 | action <= 4; // PUSH 92 | din <= val_bits == 0 ? 32'hffff : val_bits - 1; 93 | end 94 | end 95 | endcase 96 | end 97 | end 98 | 99 | // PIO instance 1 100 | pio pio_1 ( 101 | .clk(clk_25mhz), 102 | .reset(reset), 103 | .mindex(mindex), 104 | .din(din), 105 | .index(index), 106 | .action(action), 107 | .dout(dout), 108 | .gpio_in(gpio_in), 109 | .gpio_out(gpio_out), 110 | .gpio_dir(gpio_dir), 111 | .irq0(irq0), 112 | .irq1(irq1), 113 | .tx_full(tx_full), 114 | .rx_empty(rx_empty) 115 | ); 116 | 117 | // Led and gpio output 118 | assign led = {2'b11, ~gpio_out[0]}; 119 | assign gn[3] = gpio_out[0]; 120 | 121 | endmodule 122 | 123 | -------------------------------------------------------------------------------- /src/top/pwm2.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("pwm.mem", program); 40 | 41 | reg [35:0] conf1 [0:31]; 42 | reg [35:0] conf2 [0:31]; 43 | wire [5:0] clen = 10; // Config length 44 | initial $readmemh("pwm_conf1.mem", conf1); 45 | initial $readmemh("pwm_conf2.mem", conf2); 46 | 47 | // State machine to send program to PIO and configure PIO state machines 48 | reg [1:0] state; 49 | reg [4:0] cindex; 50 | reg [4:0] pindex; 51 | 52 | reg [26:0] delay_cnt; 53 | reg [3:0] val; 54 | reg m2; 55 | 56 | wire [3:0] val_bits = delay_cnt[26:23]; 57 | 58 | always @(posedge clk_25mhz) begin 59 | if (reset) begin 60 | din <= 0; 61 | action <= 0; 62 | index <= 0; 63 | mindex <= 0; 64 | gpio_in <= 0; 65 | state <= 0; 66 | cindex <= 0; 67 | pindex <= 0; 68 | end else begin 69 | case (state) 70 | 0: begin // Send program to pio 71 | action <= 1; 72 | din <= program[pindex]; 73 | pindex <= pindex + 1; 74 | index <= pindex; 75 | if (pindex == 31) 76 | state <= 1; 77 | end 78 | 1: begin // Configure machine 1 79 | cindex <= cindex + 1; 80 | if (cindex == clen) begin 81 | state <= 2; 82 | action <= 0; 83 | mindex <= 1; 84 | cindex <= 0; 85 | end else begin 86 | action <= conf1[cindex][35:32]; 87 | din <= conf1[cindex][31:0]; 88 | end 89 | end 90 | 2: begin // Configure machine 2 91 | cindex <= cindex + 1; 92 | if (cindex == clen) begin 93 | state <= 3; 94 | action <= 0; 95 | end else begin 96 | action <= conf2[cindex][35:32]; 97 | din <= conf2[cindex][31:0]; 98 | end 99 | end 100 | 3: begin // Run state 101 | delay_cnt <= delay_cnt + 1; 102 | val <= val_bits; 103 | action <= 0; 104 | m2 <= 0; 105 | // Send new value when top 4 bits change 106 | if (val_bits != val) begin 107 | mindex <= 0; 108 | action <= 4; // PUSH 109 | din <= val_bits == 0 ? 32'hffff : val_bits - 1; 110 | m2 <= 1; 111 | end else if (m2) begin // Push to machine 2 on next cycle 112 | mindex <= 1; 113 | action <= 4; 114 | end 115 | end 116 | endcase 117 | end 118 | end 119 | 120 | // PIO instance 1 121 | pio pio_1 ( 122 | .clk(clk_25mhz), 123 | .reset(reset), 124 | .mindex(mindex), 125 | .din(din), 126 | .index(index), 127 | .action(action), 128 | .dout(dout), 129 | .gpio_in(gpio_in), 130 | .gpio_out(gpio_out), 131 | .gpio_dir(gpio_dir), 132 | .irq0(irq0), 133 | .irq1(irq1), 134 | .tx_full(tx_full), 135 | .rx_empty(rx_empty) 136 | ); 137 | 138 | // Led and gpio output 139 | `ifdef blackicemx 140 | assign led = ~gpio_out[1:0]; 141 | `else 142 | assign led = gpio_out[1:0]; 143 | `endif 144 | 145 | assign gn[3] = gpio_out[0]; 146 | assign gn[2] = gpio_out[1]; 147 | 148 | endmodule 149 | 150 | -------------------------------------------------------------------------------- /src/top/pwm_conf.mem: -------------------------------------------------------------------------------- 1 | 240006000 // Set wrap 2 | 700000280 // Set divider to get 10MHz frequency 3 | 540000000 // Set side pin group to pin 0 4 | 400000010 // Set period to 16 5 | 000000010 // Maintain din for extra clock cycle 6 | 900008080 // Execute pull 7 | 000008080 // Maintain din 8 | 90000a0c7 // Execute mov isr, osr 9 | 00000a0c7 // Maintain din 10 | 600000001 // Enable machine 1 11 | 12 | -------------------------------------------------------------------------------- /src/top/pwm_conf1.mem: -------------------------------------------------------------------------------- 1 | 240006000 // Set wrap 2 | 700000280 // Set divider to get 10MHz frequency 3 | 540000000 // Set side pin group to pin 0 4 | 400000010 // Set period to 16 5 | 000000010 // Maintain din for extra clock cycle 6 | 900008080 // Execute pull 7 | 000008080 // Maintain din 8 | 90000a0c7 // Execute mov isr, osr 9 | 00000a0c7 // Maintain din 10 | 600000001 // Enable machine 1 11 | 12 | -------------------------------------------------------------------------------- /src/top/pwm_conf2.mem: -------------------------------------------------------------------------------- 1 | 240006000 // Set wrap and sideset enable bit 2 | 700000280 // Set divider to get 10MHz frequency 3 | 540000400 // Set side pin group to pin 1 4 | 400000010 // Set period to 16 5 | 000000010 // Maintain din for extra clock cycle 6 | 900008080 // Execute pull 7 | 000008080 // Maintain din 8 | 90000a0c7 // Execute mov isr, osr 9 | 00000a0c7 // Maintain din 10 | 600000003 // Enable machines 1 and 2 11 | 12 | -------------------------------------------------------------------------------- /src/top/rx_conf.mem: -------------------------------------------------------------------------------- 1 | 200003000 // Set wrap 2 | 700001b00 // Set divider 27 3 | A00850000 // Autopush and threshold 8, shift right 4 | 500000000 // IN pin 0 5 | 600000001 // Enable machine 1 6 | 7 | -------------------------------------------------------------------------------- /src/top/sq_conf.mem: -------------------------------------------------------------------------------- 1 | 200001000 // Set wrap 2 | 700000C80 // Set divider 3 | 504000000 // Set pin groups, SET pin 0 4 | 600000001 // Enable machine 1 5 | 6 | -------------------------------------------------------------------------------- /src/top/square.mem: -------------------------------------------------------------------------------- 1 | e001 2 | e000 3 | -------------------------------------------------------------------------------- /src/top/square.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("square.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 4; // Config length 43 | initial $readmemh("sq_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | always @(posedge clk_25mhz) begin 51 | if (reset) begin 52 | din <= 0; 53 | action <= 0; 54 | index <= 0; 55 | mindex <= 0; 56 | gpio_in <= 0; 57 | state <= 0; 58 | cindex <= 0; 59 | pindex <= 0; 60 | end else begin 61 | case (state) 62 | 0: begin // Send program to pio 63 | action <= 1; 64 | din <= program[pindex]; 65 | pindex <= pindex + 1; 66 | index <= pindex; 67 | if (pindex == 31) 68 | state <= 1; 69 | end 70 | 1: begin // Do configuration 71 | cindex <= cindex + 1; 72 | if (cindex == clen) begin 73 | state <= 2; 74 | action <= 0; 75 | end else begin 76 | action <= conf[cindex][35:32]; 77 | din <= conf[cindex][31:0]; 78 | end 79 | end 80 | 2: begin // Run state 81 | end 82 | endcase 83 | end 84 | end 85 | 86 | // PIO instance 1 87 | pio pio_1 ( 88 | .clk(clk_25mhz), 89 | .reset(reset), 90 | .mindex(mindex), 91 | .din(din), 92 | .index(index), 93 | .action(action), 94 | .dout(dout), 95 | .gpio_in(gpio_in), 96 | .gpio_out(gpio_out), 97 | .gpio_dir(gpio_dir), 98 | .irq0(irq0), 99 | .irq1(irq1), 100 | .tx_full(tx_full), 101 | .rx_empty(rx_empty) 102 | ); 103 | 104 | // Led and gpio outpuy 105 | assign led = {reset, gpio_out[0]}; 106 | assign gn[3] = gpio_out[0]; 107 | assign tx = 0; 108 | 109 | endmodule 110 | 111 | -------------------------------------------------------------------------------- /src/top/st_conf.mem: -------------------------------------------------------------------------------- 1 | 20000A500 // Set wrap 11 and wrap_target 10 2 | 7003FFF00 // Set divider 16K 3 | 500400000 // Set OUT count to 4 4 | A00000000 // Shift control, shift left 5 | 600000001 // Enable machine 1 6 | 7 | -------------------------------------------------------------------------------- /src/top/stepper.mem: -------------------------------------------------------------------------------- 1 | 80a0 2 | a0c7 3 | 80a0 4 | a047 5 | a0e6 6 | e026 7 | 6204 8 | 0046 9 | 6004 10 | 0084 11 | 6004 12 | 000a 13 | -------------------------------------------------------------------------------- /src/top/stepper.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("stepper.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 5; // Config length 43 | initial $readmemh("st_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | reg [2:0] delay_cnt; 51 | 52 | always @(posedge clk_25mhz) begin 53 | if (reset) begin 54 | din <= 0; 55 | action <= 0; 56 | index <= 0; 57 | mindex <= 0; 58 | gpio_in <= 0; 59 | state <= 0; 60 | cindex <= 0; 61 | pindex <= 0; 62 | end else begin 63 | case (state) 64 | 0: begin // Send program to pio 65 | action <= 1; 66 | din <= program[pindex]; 67 | pindex <= pindex + 1; 68 | index <= pindex; 69 | if (pindex == 31) 70 | state <= 1; 71 | end 72 | 1: begin // Do configuration 73 | cindex <= cindex + 1; 74 | if (cindex == clen) begin 75 | state <= 2; 76 | action <= 0; 77 | end else begin 78 | action <= conf[cindex][35:32]; 79 | din <= conf[cindex][31:0]; 80 | end 81 | end 82 | 2: begin // Run state 83 | if (delay_cnt < 4) delay_cnt <= delay_cnt + 1; 84 | if (delay_cnt == 0) begin 85 | action <= 4; 86 | din <= 332'b1000_1100_0100_0110_0010_0011_0001_1001; // Set phases 87 | end else if (delay_cnt == 2) begin 88 | din <= 1000000; // Set number of half steps 89 | end else if (delay_cnt == 4) begin 90 | action <= 0; 91 | end 92 | end 93 | endcase 94 | end 95 | end 96 | 97 | // PIO instance 1 98 | pio pio_1 ( 99 | .clk(clk_25mhz), 100 | .reset(reset), 101 | .mindex(mindex), 102 | .din(din), 103 | .index(index), 104 | .action(action), 105 | .dout(dout), 106 | .gpio_in(gpio_in), 107 | .gpio_out(gpio_out), 108 | .gpio_dir(gpio_dir), 109 | .irq0(irq0), 110 | .irq1(irq1), 111 | .tx_full(tx_full), 112 | .rx_empty(rx_empty) 113 | ); 114 | 115 | // Led and gpio output 116 | assign led = {2'b11, ~gpio_out[0]}; 117 | assign gn[3:0] = gpio_out[3:0]; 118 | 119 | endmodule 120 | 121 | -------------------------------------------------------------------------------- /src/top/tomem.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | with open(sys.argv[1], "rb") as f: 4 | byte = f.read(4) 5 | while byte != b"": 6 | print("".join("%02x%02x%02x%02x" % (byte[0], byte[1], byte[2], byte[3]))) 7 | byte = f.read(4) 8 | -------------------------------------------------------------------------------- /src/top/top.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("uart_tx.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 6; // Config length 43 | initial $readmemh("tx_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | reg [11:0] delay_cnt; 51 | reg [3:0] cp; 52 | reg [2:0] stalled; 53 | 54 | always @(posedge clk_25mhz) begin 55 | if (reset) begin 56 | din <= 0; 57 | action <= 0; 58 | index <= 0; 59 | mindex <= 0; 60 | gpio_in <= 0; 61 | state <= 0; 62 | cindex <= 0; 63 | pindex <= 0; 64 | stalled <= 0; 65 | end else begin 66 | case (state) 67 | 0: begin // Send program to pio 68 | action <= 1; 69 | din <= program[pindex]; 70 | pindex <= pindex + 1; 71 | index <= pindex; 72 | if (pindex == 31) 73 | state <= 1; 74 | end 75 | 1: begin // Do configuration 76 | cindex <= cindex + 1; 77 | if (cindex == clen) begin 78 | state <= 2; 79 | action <= 0; 80 | end else begin 81 | action <= conf[cindex][35:32]; 82 | din <= conf[cindex][31:0]; 83 | end 84 | end 85 | 2: begin // Run state 86 | if (tx_full[0]) stalled <= stalled + 1; 87 | delay_cnt <= delay_cnt + 1; 88 | if (delay_cnt == 0 && !tx_full[0]) begin 89 | action <= 4; // PUSH 90 | cp <= cp + 1; 91 | if (cp == 10) begin 92 | din <= 10; 93 | cp <= 0; 94 | end else begin 95 | din <= 32'h30 + cp; 96 | end 97 | end else if (delay_cnt == 1) begin 98 | action <= 0; 99 | end 100 | end 101 | endcase 102 | end 103 | end 104 | 105 | // PIO instance 1 106 | pio pio_1 ( 107 | .clk(clk_25mhz), 108 | .reset(reset), 109 | .mindex(mindex), 110 | .din(din), 111 | .index(index), 112 | .action(action), 113 | .dout(dout), 114 | .gpio_in(gpio_in), 115 | .gpio_out(gpio_out), 116 | .gpio_dir(gpio_dir), 117 | .irq0(irq0), 118 | .irq1(irq1), 119 | .tx_full(tx_full), 120 | .rx_empty(rx_empty) 121 | ); 122 | 123 | // Led and gpio outpuy 124 | assign led = ~stalled; 125 | assign tx = gpio_out[0]; 126 | 127 | endmodule 128 | 129 | -------------------------------------------------------------------------------- /src/top/tx_conf.mem: -------------------------------------------------------------------------------- 1 | 240007200 // Set wrap, wrap_target and sideset enable bit 2 | 700001b00 // Set divider 27 3 | 540100000 // Set pin groups side and out, sideset enable bit 4 | A00080000 // Shift control, shift out right 5 | 900000004 // Jump to 4 6 | 600000001 // Enable machine 1 7 | 8 | -------------------------------------------------------------------------------- /src/top/tx_conf2.mem: -------------------------------------------------------------------------------- 1 | 240007200 // Set wrap, wrap_target and sideset enable bit 2 | 700001b00 // Set divider 27 3 | 540100000 // Set pin groups side and out, sideset enable bit 4 | A00080000 // Shift control, shift out right 5 | 900000004 // Jump to 4 6 | 600000003 // Enable machine 1 and 2 7 | 8 | -------------------------------------------------------------------------------- /src/top/uart_rx.mem: -------------------------------------------------------------------------------- 1 | 2020 2 | ea27 3 | 4001 4 | 0642 5 | -------------------------------------------------------------------------------- /src/top/uart_rx.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | `ifdef lcd_diag 7 | // SPI display 8 | output oled_csn, 9 | output oled_clk, 10 | output oled_mosi, 11 | output oled_dc, 12 | output oled_resn, 13 | `endif 14 | // Leds 15 | output reg [7:0] led, 16 | output [27:0] gn, 17 | input rx, 18 | output tx 19 | ); 20 | 21 | // PIO registers and wires 22 | reg [31:0] din; // Data sent to PIO 23 | reg [4:0] index; // Instruction index 24 | reg [3:0] action; // Action to be done by PIO 25 | reg [1:0] mindex; // Machine index 26 | wire [31:0] gpio_in; // Input pins to PIO 27 | 28 | wire [31:0] gpio_out; // Output pins from PIO 29 | wire [31:0] gpio_dir; // Pin directions 30 | wire [31:0] dout; // Output from PIO 31 | wire irq0, irq1; // IRQ flags from PIO 32 | wire [3:0] tx_full; // Set when TX fifo is full 33 | wire [3:0] rx_empty; // Set when RX fifo is empty 34 | 35 | reg [7:0] data; 36 | 37 | // Power-on reset 38 | reg [15:0] pwr_up_reset_counter = 0; 39 | wire pwr_up_reset_n = &pwr_up_reset_counter; 40 | wire n_reset = pwr_up_reset_n & btn[0]; 41 | wire reset = ~n_reset; 42 | 43 | assign gpio_in = {31'b0, rx}; 44 | 45 | always @(posedge clk_25mhz) begin 46 | if (!pwr_up_reset_n) 47 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 48 | end 49 | 50 | `ifdef lcd_diag 51 | // =============================================================== 52 | // System Clock generation 53 | // =============================================================== 54 | wire clk_sdram_locked; 55 | wire [3:0] clocks; 56 | ecp5pll 57 | #( 58 | .in_hz( 25*1000000), 59 | .out0_hz(125*1000000), 60 | .out1_hz( 25*1000000), 61 | .out2_hz(100*1000000), // SDRAM core 62 | .out3_hz(100*1000000), .out3_deg(180) // SDRAM chip 45-330:ok 0-30:not 63 | ) 64 | ecp5pll_inst 65 | ( 66 | .clk_i(clk_25mhz), 67 | .clk_o(clocks), 68 | .locked(clk_sdram_locked) 69 | ); 70 | wire clk_hdmi = clocks[0]; 71 | wire clk_vga = clocks[1]; 72 | wire clk_cpu = clocks[1]; 73 | wire clk_sdram = clocks[2]; 74 | wire sdram_clk = clocks[3]; // phase shifted for chip 75 | `endif 76 | 77 | // Configuration of state machines and program instructions 78 | reg [15:0] program [0:31]; 79 | initial $readmemh("uart_rx.mem", program); 80 | 81 | reg [35:0] conf [0:31]; 82 | wire [5:0] clen = 5; // Config length 83 | initial $readmemh("rx_conf.mem", conf); 84 | 85 | // State machine to send program to PIO and configure PIO state machines 86 | reg [1:0] state; 87 | reg [4:0] cindex; 88 | reg [4:0] pindex; 89 | reg [1:0] delay_cnt; 90 | 91 | always @(posedge clk_25mhz) begin 92 | if (reset) begin 93 | din <= 0; 94 | action <= 0; 95 | index <= 0; 96 | mindex <= 0; 97 | state <= 0; 98 | cindex <= 0; 99 | pindex <= 0; 100 | end else begin 101 | case (state) 102 | 0: begin // Send program to pio 103 | action <= 1; 104 | din <= program[pindex]; 105 | pindex <= pindex + 1; 106 | index <= pindex; 107 | if (pindex == 31) 108 | state <= 1; 109 | end 110 | 1: begin // Do configuration 111 | cindex <= cindex + 1; 112 | if (cindex == clen) begin 113 | state <= 2; 114 | action <= 0; 115 | end else begin 116 | action <= conf[cindex][35:32]; 117 | din <= conf[cindex][31:0]; 118 | end 119 | end 120 | 2: begin // Run state 121 | action <= 0; 122 | delay_cnt <= delay_cnt + 1; 123 | if (!rx_empty[0] && &delay_cnt) begin 124 | action <= 3; // Pull 125 | end 126 | data <= dout[31:24]; 127 | end 128 | endcase 129 | end 130 | end 131 | 132 | wire [127:0] diag; 133 | 134 | // PIO instance 1 135 | pio pio_1 ( 136 | .clk(clk_25mhz), 137 | .reset(reset), 138 | .mindex(mindex), 139 | .din(din), 140 | .index(index), 141 | .action(action), 142 | .dout(dout), 143 | .gpio_in(gpio_in), 144 | .gpio_out(gpio_out), 145 | .gpio_dir(gpio_dir), 146 | .irq0(irq0), 147 | .irq1(irq1), 148 | .tx_full(tx_full), 149 | .rx_empty(rx_empty) 150 | ); 151 | 152 | // Led and gpio output 153 | always @(posedge clk_25mhz) led <= data; 154 | assign tx = gpio_out[0]; 155 | 156 | `ifdef lcd_diag 157 | localparam c_lcd_hex = 1; 158 | 159 | reg [6:0] r_btn_joy; 160 | always @(posedge clk_25mhz) r_btn_joy <= btn; 161 | 162 | // =============================================================== 163 | // LCD diagnostics 164 | // =============================================================== 165 | generate 166 | if(c_lcd_hex) begin 167 | // SPI DISPLAY 168 | reg [127:0] r_display; 169 | // HEX decoder does printf("%16X\n%16X\n", r_display[63:0], r_display[127:64]); 170 | always @(posedge clk_25mhz) 171 | r_display <= diag; 172 | 173 | parameter c_color_bits = 16; 174 | wire [7:0] x; 175 | wire [7:0] y; 176 | wire [c_color_bits-1:0] color; 177 | hex_decoder_v 178 | #( 179 | .c_data_len(128), 180 | .c_row_bits(4), 181 | .c_grid_6x8(1), // NOTE: TRELLIS needs -abc9 option to compile 182 | .c_font_file("hex_font.mem"), 183 | .c_color_bits(c_color_bits) 184 | ) 185 | hex_decoder_v_inst 186 | ( 187 | .clk(clk_hdmi), 188 | .data(r_display), 189 | .x(x[7:1]), 190 | .y(y[7:1]), 191 | .color(color) 192 | ); 193 | 194 | wire next_pixel; 195 | reg [c_color_bits-1:0] r_color; 196 | wire w_oled_csn; 197 | 198 | always @(posedge clk_hdmi) 199 | if(next_pixel) r_color <= color; 200 | 201 | lcd_video #( 202 | .c_clk_mhz(125), 203 | .c_init_file("st7789_linit_xflip.mem"), 204 | .c_clk_phase(0), 205 | .c_clk_polarity(1), 206 | .c_init_size(38) 207 | ) lcd_video_inst ( 208 | .clk(clk_hdmi), 209 | .reset(r_btn_joy[5]), 210 | .x(x), 211 | .y(y), 212 | .next_pixel(next_pixel), 213 | .color(r_color), 214 | .spi_clk(oled_clk), 215 | .spi_mosi(oled_mosi), 216 | .spi_dc(oled_dc), 217 | .spi_resn(oled_resn), 218 | .spi_csn(w_oled_csn) 219 | ); 220 | 221 | //assign oled_csn = w_oled_csn; // 8-pin ST7789: oled_csn is connected to CSn 222 | assign oled_csn = 1; // 7-pin ST7789: oled_csn is connected to BLK (backlight enable pin) 223 | end 224 | endgenerate 225 | `endif 226 | 227 | endmodule 228 | 229 | -------------------------------------------------------------------------------- /src/top/uart_tx.mem: -------------------------------------------------------------------------------- 1 | 98a0 2 | f727 3 | 6001 4 | 0642 5 | -------------------------------------------------------------------------------- /src/top/uart_tx.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | wire [4:0] offset = 4; 27 | 28 | // Power-on reset 29 | reg [15:0] pwr_up_reset_counter = 0; 30 | wire pwr_up_reset_n = &pwr_up_reset_counter; 31 | wire n_reset = pwr_up_reset_n & btn[0]; 32 | wire reset = ~n_reset; 33 | 34 | always @(posedge clk_25mhz) begin 35 | if (!pwr_up_reset_n) 36 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 37 | end 38 | 39 | // Configuration of state machines and program instructions 40 | reg [15:0] program [0:31]; 41 | initial $readmemh("uart_tx.mem", program); 42 | 43 | reg [35:0] conf [0:31]; 44 | wire [5:0] clen = 6; // Config length 45 | initial $readmemh("tx_conf.mem", conf); 46 | 47 | // State machine to send program to PIO and configure PIO state machines 48 | reg [1:0] state; 49 | reg [4:0] cindex; 50 | reg [4:0] pindex; 51 | 52 | reg [11:0] delay_cnt; 53 | reg [3:0] cp; 54 | reg [2:0] stalled; 55 | 56 | always @(posedge clk_25mhz) begin 57 | if (reset) begin 58 | din <= 0; 59 | action <= 0; 60 | index <= 0; 61 | mindex <= 0; 62 | gpio_in <= 0; 63 | state <= 0; 64 | cindex <= 0; 65 | pindex <= 0; 66 | stalled <= 0; 67 | end else begin 68 | case (state) 69 | 0: begin // Send program to pio 70 | action <= 1; 71 | // Add offset to jumps 72 | din <= program[pindex][15:13] == 0 ? program[pindex] + offset : program[pindex]; 73 | pindex <= pindex + 1; 74 | index <= pindex + offset; 75 | if (pindex == 31) 76 | state <= 1; 77 | end 78 | 1: begin // Do configuration 79 | cindex <= cindex + 1; 80 | if (cindex == clen) begin 81 | state <= 2; 82 | action <= 0; 83 | end else begin 84 | action <= conf[cindex][35:32]; 85 | din <= conf[cindex][31:0]; 86 | end 87 | end 88 | 2: begin // Run state 89 | if (tx_full[0]) stalled <= stalled + 1; 90 | delay_cnt <= delay_cnt + 1; 91 | if (delay_cnt == 0 && !tx_full[0]) begin 92 | action <= 4; // PUSH 93 | cp <= cp + 1; 94 | if (cp == 10) begin 95 | din <= 10; 96 | cp <= 0; 97 | end else begin 98 | din <= 32'h30 + cp; 99 | end 100 | end else if (delay_cnt == 1) begin 101 | action <= 0; 102 | end 103 | end 104 | endcase 105 | end 106 | end 107 | 108 | // PIO instance 1 109 | pio pio_1 ( 110 | .clk(clk_25mhz), 111 | .reset(reset), 112 | .mindex(mindex), 113 | .din(din), 114 | .index(index), 115 | .action(action), 116 | .dout(dout), 117 | .gpio_in(gpio_in), 118 | .gpio_out(gpio_out), 119 | .gpio_dir(gpio_dir), 120 | .irq0(irq0), 121 | .irq1(irq1), 122 | .tx_full(tx_full), 123 | .rx_empty(rx_empty) 124 | ); 125 | 126 | // Led and gpio outpuy 127 | assign led = ~stalled; 128 | assign tx = gpio_out[0]; 129 | 130 | endmodule 131 | 132 | -------------------------------------------------------------------------------- /src/top/ws2812.mem: -------------------------------------------------------------------------------- 1 | 6121 2 | 1123 3 | 1100 4 | a142 5 | -------------------------------------------------------------------------------- /src/top/ws2812.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module top ( 3 | input clk_25mhz, 4 | // Buttons 5 | input [6:0] btn, 6 | // Leds 7 | output [7:0] led, 8 | output [27:0] gn, 9 | output tx 10 | ); 11 | 12 | // PIO registers and wires 13 | reg [31:0] din; // Data sent to PIO 14 | reg [4:0] index; // Instruction index 15 | reg [3:0] action; // Action to be done by PIO 16 | reg [1:0] mindex; // Machine index 17 | reg [31:0] gpio_in; // Input pins to PIO 18 | 19 | wire [31:0] gpio_out; // Output pins from PIO 20 | wire [31:0] gpio_dir; // Pin directions 21 | wire [31:0] dout; // Output from PIO 22 | wire irq0, irq1; // IRQ flags from PIO 23 | wire [3:0] tx_full; // Set when TX fifo is full 24 | wire [3:0] rx_empty; // Set when RX fifo is empty 25 | 26 | // Power-on reset 27 | reg [15:0] pwr_up_reset_counter = 0; 28 | wire pwr_up_reset_n = &pwr_up_reset_counter; 29 | wire n_reset = pwr_up_reset_n & btn[0]; 30 | wire reset = ~n_reset; 31 | 32 | always @(posedge clk_25mhz) begin 33 | if (!pwr_up_reset_n) 34 | pwr_up_reset_counter <= pwr_up_reset_counter + 1; 35 | end 36 | 37 | // Configuration of state machines and program instructions 38 | reg [15:0] program [0:31]; 39 | initial $readmemh("ws2812.mem", program); 40 | 41 | reg [35:0] conf [0:31]; 42 | wire [5:0] clen = 5; // Config length 43 | initial $readmemh("ws_conf.mem", conf); 44 | 45 | // State machine to send program to PIO and configure PIO state machines 46 | reg [1:0] state; 47 | reg [4:0] cindex; 48 | reg [4:0] pindex; 49 | 50 | reg [11:0] delay_cnt; 51 | reg [3:0] cp; 52 | reg [2:0] stalled; 53 | reg [4:0] pix_cnt; 54 | reg [11:0] outer; 55 | reg [3:0] target; 56 | 57 | always @(posedge clk_25mhz) begin 58 | if (reset) begin 59 | din <= 0; 60 | action <= 0; 61 | index <= 0; 62 | mindex <= 0; 63 | gpio_in <= 0; 64 | state <= 0; 65 | cindex <= 0; 66 | pindex <= 0; 67 | stalled <= 0; 68 | end else begin 69 | case (state) 70 | 0: begin // Send program to pio 71 | action <= 1; 72 | din <= program[pindex]; 73 | pindex <= pindex + 1; 74 | index <= pindex; 75 | if (pindex == 31) 76 | state <= 1; 77 | end 78 | 1: begin // Do configuration 79 | cindex <= cindex + 1; 80 | if (cindex == clen) begin 81 | state <= 2; 82 | action <= 0; 83 | end else begin 84 | action <= conf[cindex][35:32]; 85 | din <= conf[cindex][31:0]; 86 | end 87 | end 88 | 2: begin // Run state 89 | if (tx_full[0]) stalled <= stalled + 1; 90 | delay_cnt <= delay_cnt + 1; 91 | if (delay_cnt == 0 && !tx_full[0] && pix_cnt <= 15) begin 92 | action <= 4; // PUSH 93 | din = pix_cnt == target ? 32'h00ff0000 : 32'hff00ff00; 94 | end else if (delay_cnt == 1) begin 95 | action <= 0; 96 | end else if (delay_cnt == 800) begin 97 | if (pix_cnt < 16) begin 98 | pix_cnt <= pix_cnt + 1; 99 | end else begin 100 | outer <= outer + 1; 101 | if (&outer) begin 102 | pix_cnt <= 0; 103 | target <= target + 1; 104 | end 105 | end 106 | delay_cnt <= 0; 107 | end 108 | end 109 | endcase 110 | end 111 | end 112 | 113 | // PIO instance 1 114 | pio pio_1 ( 115 | .clk(clk_25mhz), 116 | .reset(reset), 117 | .mindex(mindex), 118 | .din(din), 119 | .index(index), 120 | .action(action), 121 | .dout(dout), 122 | .gpio_in(gpio_in), 123 | .gpio_out(gpio_out), 124 | .gpio_dir(gpio_dir), 125 | .irq0(irq0), 126 | .irq1(irq1), 127 | .tx_full(tx_full), 128 | .rx_empty(rx_empty) 129 | ); 130 | 131 | // Led and gpio outpuy 132 | assign led = ~stalled; 133 | assign gn[3] = gpio_out[0]; 134 | 135 | endmodule 136 | 137 | -------------------------------------------------------------------------------- /src/top/ws_conf.mem: -------------------------------------------------------------------------------- 1 | 200003000 // Set wrap 2 | 700000535 // Set divider 5.208 3 | 520000000 // Set pin group side 4 | A30020000 // autopull threshold 24 and shift left 5 | 600000001 // Enable machine 1 6 | 7 | -------------------------------------------------------------------------------- /ulx3s/Makefile: -------------------------------------------------------------------------------- 1 | TOP ?= top 2 | 3 | VERILOG = ../src/top/${TOP}.v ../src/pio.v ../src/machine.v \ 4 | ../src/pc.v ../src/decoder.v ../src/divider.v \ 5 | ../src/isr.v ../src/osr.v ../src/scratch.v ../src/fifo.v 6 | 7 | PIN_DEF = ulx3s_v20.lpf 8 | 9 | include ulx3s.mk 10 | 11 | -------------------------------------------------------------------------------- /ulx3s/ulx3s.mk: -------------------------------------------------------------------------------- 1 | PIN_DEF ?= ulx3s_v20.lpf 2 | 3 | DEVICE ?= 85k 4 | 5 | BUILDDIR = bin 6 | 7 | compile: $(BUILDDIR)/toplevel.bit 8 | 9 | prog: $(BUILDDIR)/toplevel.bit 10 | ujprog $^ 11 | 12 | $(BUILDDIR)/toplevel.json: $(VERILOG) 13 | mkdir -p $(BUILDDIR) 14 | yosys -f "verilog -Dulx3s" -p "synth_ecp5 -json $@" $^ 15 | 16 | $(BUILDDIR)/%.config: $(PIN_DEF) $(BUILDDIR)/toplevel.json 17 | nextpnr-ecp5 --${DEVICE} --package CABGA381 --timing-allow-fail --freq 25 --textcfg $@ --json $(filter-out $<,$^) --lpf $< 18 | 19 | $(BUILDDIR)/toplevel.bit: $(BUILDDIR)/toplevel.config 20 | ecppack --compress $^ $@ 21 | 22 | clean: 23 | rm -rf ${BUILDDIR} 24 | 25 | .SECONDARY: 26 | .PHONY: compile clean prog 27 | -------------------------------------------------------------------------------- /ulx3s/ulx3s_v20.lpf: -------------------------------------------------------------------------------- 1 | BLOCK RESETPATHS; 2 | BLOCK ASYNCPATHS; 3 | ## ULX3S v2.x.x and v3.0.x 4 | 5 | # The clock "usb" and "gpdi" sheet 6 | LOCATE COMP "clk_25mhz" SITE "G2"; 7 | IOBUF PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS33; 8 | FREQUENCY PORT "clk_25mhz" 25 MHZ; 9 | 10 | # JTAG and SPI FLASH voltage 3.3V and options to boot from SPI flash 11 | # write to FLASH possible any time from JTAG: 12 | #SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=ENABLE SLAVE_PARALLEL_PORT=DISABLE; 13 | # write to FLASH possible from user bitstream: 14 | # SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE; 15 | 16 | ## USBSERIAL FTDI-FPGA serial port "usb" sheet 17 | LOCATE COMP "tx" SITE "L4"; # FPGA transmits to ftdi 18 | LOCATE COMP "rx" SITE "M1"; # FPGA receives from ftdi 19 | LOCATE COMP "ftdi_nrts" SITE "M3"; # FPGA receives 20 | LOCATE COMP "ftdi_ndtr" SITE "N1"; # FPGA receives 21 | LOCATE COMP "ftdi_txden" SITE "L3"; # FPGA receives 22 | IOBUF PORT "tx" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 23 | IOBUF PORT "rx" PULLMODE=UP IO_TYPE=LVCMOS33; 24 | IOBUF PORT "ftdi_nrts" PULLMODE=UP IO_TYPE=LVCMOS33; 25 | IOBUF PORT "ftdi_ndtr" PULLMODE=UP IO_TYPE=LVCMOS33; 26 | IOBUF PORT "ftdi_txden" PULLMODE=UP IO_TYPE=LVCMOS33; 27 | 28 | ## LED indicators "blinkey" and "gpio" sheet 29 | LOCATE COMP "led[7]" SITE "H3"; 30 | LOCATE COMP "led[6]" SITE "E1"; 31 | LOCATE COMP "led[5]" SITE "E2"; 32 | LOCATE COMP "led[4]" SITE "D1"; 33 | LOCATE COMP "led[3]" SITE "D2"; 34 | LOCATE COMP "led[2]" SITE "C1"; 35 | LOCATE COMP "led[1]" SITE "C2"; 36 | LOCATE COMP "led[0]" SITE "B2"; 37 | IOBUF PORT "led[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 38 | IOBUF PORT "led[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 39 | IOBUF PORT "led[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 40 | IOBUF PORT "led[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 41 | IOBUF PORT "led[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 42 | IOBUF PORT "led[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 43 | IOBUF PORT "led[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 44 | IOBUF PORT "led[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 45 | 46 | ## Pushbuttons "blinkey", "flash", "power", "gpdi" sheet 47 | LOCATE COMP "btn[0]" SITE "D6"; # BTN_PWRn (inverted logic) 48 | LOCATE COMP "btn[1]" SITE "R1"; # FIRE1 49 | LOCATE COMP "btn[2]" SITE "T1"; # FIRE2 50 | LOCATE COMP "btn[3]" SITE "R18"; # UP W1->R18 51 | LOCATE COMP "btn[4]" SITE "V1"; # DOWN 52 | LOCATE COMP "btn[5]" SITE "U1"; # LEFT 53 | LOCATE COMP "btn[6]" SITE "H16"; # RIGHT Y2->H16 54 | IOBUF PORT "btn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 55 | IOBUF PORT "btn[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 56 | IOBUF PORT "btn[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 57 | IOBUF PORT "btn[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 58 | IOBUF PORT "btn[4]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 59 | IOBUF PORT "btn[5]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 60 | IOBUF PORT "btn[6]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 61 | 62 | ## DIP switch "blinkey", "gpio" sheet 63 | LOCATE COMP "sw[0]" SITE "E8"; # SW1 64 | LOCATE COMP "sw[1]" SITE "D8"; # SW2 65 | LOCATE COMP "sw[2]" SITE "D7"; # SW3 66 | LOCATE COMP "sw[3]" SITE "E7"; # SW4 67 | IOBUF PORT "sw[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 68 | IOBUF PORT "sw[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 69 | IOBUF PORT "sw[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 70 | IOBUF PORT "sw[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 71 | 72 | ## SPI OLED DISPLAY SSD1331 (Color) or SSD1306 (B/W) "blinkey", "usb" sheet 73 | LOCATE COMP "oled_clk" SITE "P4"; 74 | LOCATE COMP "oled_mosi" SITE "P3"; 75 | LOCATE COMP "oled_dc" SITE "P1"; 76 | LOCATE COMP "oled_resn" SITE "P2"; 77 | LOCATE COMP "oled_csn" SITE "N2"; 78 | IOBUF PORT "oled_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 79 | IOBUF PORT "oled_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 80 | IOBUF PORT "oled_dc" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 81 | IOBUF PORT "oled_resn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 82 | IOBUF PORT "oled_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 83 | 84 | ## SPI Flash chip "flash" sheet 85 | LOCATE COMP "flash_csn" SITE "R2"; 86 | LOCATE COMP "flash_sck" SITE "U3"; 87 | LOCATE COMP "flash_mosi" SITE "W2"; 88 | LOCATE COMP "flash_miso" SITE "V2"; 89 | LOCATE COMP "flash_holdn" SITE "W1"; 90 | LOCATE COMP "flash_wpn" SITE "Y2"; 91 | #LOCATE COMP "flash_csspin" SITE "AJ3"; 92 | #LOCATE COMP "flash_initn" SITE "AG4"; 93 | #LOCATE COMP "flash_done" SITE "AJ4"; 94 | #LOCATE COMP "flash_programn" SITE "AH4"; 95 | #LOCATE COMP "flash_cfg_select[0]" SITE "AM4"; 96 | #LOCATE COMP "flash_cfg_select[1]" SITE "AL4"; 97 | #LOCATE COMP "flash_cfg_select[2]" SITE "AK4"; 98 | IOBUF PORT "flash_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 99 | IOBUF PORT "flash_sck" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 100 | IOBUF PORT "flash_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 101 | IOBUF PORT "flash_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 102 | IOBUF PORT "flash_holdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 103 | IOBUF PORT "flash_wpn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 104 | #IOBUF PORT "flash_csspin" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 105 | #IOBUF PORT "flash_initn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 106 | #IOBUF PORT "flash_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 107 | #IOBUF PORT "flash_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 108 | #IOBUF PORT "flash_cfg_select[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 109 | #IOBUF PORT "flash_cfg_select[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 110 | #IOBUF PORT "flash_cfg_select[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 111 | 112 | ## SD card "sdcard", "usb" sheet 113 | LOCATE COMP "sd_clk" SITE "H2"; # sd_clk WiFi_GPIO14 114 | LOCATE COMP "sd_cmd" SITE "J1"; # sd_cmd_di (MOSI) WiFi GPIO15 115 | LOCATE COMP "sd_d[0]" SITE "J3"; # sd_dat0_do (MISO) WiFi GPIO2 116 | LOCATE COMP "sd_d[1]" SITE "H1"; # sd_dat1_irq WiFi GPIO4 117 | LOCATE COMP "sd_d[2]" SITE "K1"; # sd_dat2 WiFi_GPIO12 118 | LOCATE COMP "sd_d[3]" SITE "K2"; # sd_dat3_csn WiFi_GPIO13 119 | LOCATE COMP "sd_wp" SITE "P5"; # not connected 120 | LOCATE COMP "sd_cdn" SITE "N5"; # not connected 121 | IOBUF PORT "sd_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 122 | IOBUF PORT "sd_cmd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 123 | IOBUF PORT "sd_d[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 124 | IOBUF PORT "sd_d[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 125 | IOBUF PORT "sd_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; # WiFi GPIO 126 | IOBUF PORT "sd_d[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 127 | IOBUF PORT "sd_wp" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 128 | IOBUF PORT "sd_cdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 129 | 130 | ## ADC SPI (MAX11123) "analog", "ram" sheet 131 | LOCATE COMP "adc_csn" SITE "R17"; 132 | LOCATE COMP "adc_mosi" SITE "R16"; 133 | LOCATE COMP "adc_miso" SITE "U16"; 134 | LOCATE COMP "adc_sclk" SITE "P17"; 135 | IOBUF PORT "adc_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 136 | IOBUF PORT "adc_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 137 | IOBUF PORT "adc_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 138 | IOBUF PORT "adc_sclk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 139 | 140 | ## Audio 4-bit DAC "analog", "gpio" sheet 141 | # 4-bit mode can drive down to 75 ohm load impedance. 142 | # Lower impedance leads to IO overload, 143 | # FPGA will stop working and need reboot. 144 | # For standard 17 ohm earphones on PCB v1.7: 145 | # use bits 2,3 as input (High-Z) and drive only bits 0,1. 146 | # PCB v2.1.2 can use full 4 bits and 16mA drive for 17 ohm earphones. 147 | LOCATE COMP "audio_l[3]" SITE "B3"; # JACK TIP (left audio) 148 | LOCATE COMP "audio_l[2]" SITE "C3"; 149 | LOCATE COMP "audio_l[1]" SITE "D3"; 150 | LOCATE COMP "audio_l[0]" SITE "E4"; 151 | LOCATE COMP "audio_r[3]" SITE "C5"; # JACK RING1 (right audio) 152 | LOCATE COMP "audio_r[2]" SITE "D5"; 153 | LOCATE COMP "audio_r[1]" SITE "B5"; 154 | LOCATE COMP "audio_r[0]" SITE "A3"; 155 | LOCATE COMP "audio_v[3]" SITE "E5"; # JACK RING2 (video or digital audio) 156 | LOCATE COMP "audio_v[2]" SITE "F5"; 157 | LOCATE COMP "audio_v[1]" SITE "F2"; 158 | LOCATE COMP "audio_v[0]" SITE "H5"; 159 | IOBUF PORT "audio_l[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 160 | IOBUF PORT "audio_l[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 161 | IOBUF PORT "audio_l[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 162 | IOBUF PORT "audio_l[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 163 | IOBUF PORT "audio_r[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 164 | IOBUF PORT "audio_r[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 165 | IOBUF PORT "audio_r[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 166 | IOBUF PORT "audio_r[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 167 | IOBUF PORT "audio_v[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 168 | IOBUF PORT "audio_v[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 169 | IOBUF PORT "audio_v[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 170 | IOBUF PORT "audio_v[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 171 | 172 | ## WiFi ESP-32 "wifi", "usb", "flash" sheet 173 | # other pins are shared with GP/GN, SD card and JTAG 174 | LOCATE COMP "wifi_en" SITE "F1"; # enable/reset WiFi 175 | LOCATE COMP "wifi_rxd" SITE "K3"; # FPGA transmits to WiFi 176 | LOCATE COMP "wifi_txd" SITE "K4"; # FPGA receives from WiFi 177 | LOCATE COMP "wifi_gpio0" SITE "L2"; 178 | LOCATE COMP "wifi_gpio5" SITE "N4"; # WIFI LED 179 | LOCATE COMP "wifi_gpio16" SITE "L1"; # Serial1 RX 180 | LOCATE COMP "wifi_gpio17" SITE "N3"; # Serial1 TX 181 | # LOCATE COMP "prog_done" SITE "Y3"; # not GPIO, always active 182 | IOBUF PORT "wifi_en" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 183 | IOBUF PORT "wifi_rxd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 184 | IOBUF PORT "wifi_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 185 | IOBUF PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 186 | IOBUF PORT "wifi_gpio5" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 187 | IOBUF PORT "wifi_gpio16" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 188 | IOBUF PORT "wifi_gpio17" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 189 | # IOBUF PORT "prog_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 190 | 191 | ## PCB antenna 433 MHz (may be also used for FM) "usb" sheet 192 | LOCATE COMP "ant_433mhz" SITE "G1"; 193 | IOBUF PORT "ant_433mhz" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 194 | 195 | ## Second USB port "US2" going directly into FPGA "usb", "ram" sheet 196 | LOCATE COMP "usb_fpga_dp" SITE "E16"; # single ended or differential input only 197 | LOCATE COMP "usb_fpga_dn" SITE "F16"; 198 | IOBUF PORT "usb_fpga_dp" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=16; 199 | IOBUF PORT "usb_fpga_dn" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=16; 200 | LOCATE COMP "usb_fpga_bd_dp" SITE "D15"; # single-ended bidirectional 201 | LOCATE COMP "usb_fpga_bd_dn" SITE "E15"; 202 | IOBUF PORT "usb_fpga_bd_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 203 | IOBUF PORT "usb_fpga_bd_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 204 | LOCATE COMP "usb_fpga_pu_dp" SITE "B12"; # pull up/down control 205 | LOCATE COMP "usb_fpga_pu_dn" SITE "C12"; 206 | IOBUF PORT "usb_fpga_pu_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 207 | IOBUF PORT "usb_fpga_pu_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 208 | 209 | ## JTAG ESP-32 "usb" sheet 210 | # connected to FT231X and ESP-32 211 | # commented out because those are dedicated pins, not directly useable as GPIO 212 | # but could be used by some vendor-specific JTAG bridging (boundary scan) module 213 | #LOCATE COMP "jtag_tdi" SITE "R5"; # FTDI_nRI FPGA receives 214 | #LOCATE COMP "jtag_tdo" SITE "V4"; # FTDI_nCTS FPGA transmits 215 | #LOCATE COMP "jtag_tck" SITE "T5"; # FTDI_nDSR FPGA receives 216 | #LOCATE COMP "jtag_tms" SITE "U5"; # FTDI_nDCD FPGA receives 217 | #IOBUF PORT "jtag_tdi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 218 | #IOBUF PORT "jtag_tdo" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 219 | #IOBUF PORT "jtag_tck" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 220 | #IOBUF PORT "jtag_tms" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 221 | 222 | ## SDRAM "ram" sheet 223 | LOCATE COMP "sdram_clk" SITE "F19"; 224 | LOCATE COMP "sdram_cke" SITE "F20"; 225 | LOCATE COMP "sdram_csn" SITE "P20"; 226 | LOCATE COMP "sdram_wen" SITE "T20"; 227 | LOCATE COMP "sdram_rasn" SITE "R20"; 228 | LOCATE COMP "sdram_casn" SITE "T19"; 229 | LOCATE COMP "sdram_a[0]" SITE "M20"; 230 | LOCATE COMP "sdram_a[1]" SITE "M19"; 231 | LOCATE COMP "sdram_a[2]" SITE "L20"; 232 | LOCATE COMP "sdram_a[3]" SITE "L19"; 233 | LOCATE COMP "sdram_a[4]" SITE "K20"; 234 | LOCATE COMP "sdram_a[5]" SITE "K19"; 235 | LOCATE COMP "sdram_a[6]" SITE "K18"; 236 | LOCATE COMP "sdram_a[7]" SITE "J20"; 237 | LOCATE COMP "sdram_a[8]" SITE "J19"; 238 | LOCATE COMP "sdram_a[9]" SITE "H20"; 239 | LOCATE COMP "sdram_a[10]" SITE "N19"; 240 | LOCATE COMP "sdram_a[11]" SITE "G20"; 241 | LOCATE COMP "sdram_a[12]" SITE "G19"; 242 | LOCATE COMP "sdram_ba[0]" SITE "P19"; 243 | LOCATE COMP "sdram_ba[1]" SITE "N20"; 244 | LOCATE COMP "sdram_dqm[0]" SITE "U19"; 245 | LOCATE COMP "sdram_dqm[1]" SITE "E20"; 246 | LOCATE COMP "sdram_d[0]" SITE "J16"; 247 | LOCATE COMP "sdram_d[1]" SITE "L18"; 248 | LOCATE COMP "sdram_d[2]" SITE "M18"; 249 | LOCATE COMP "sdram_d[3]" SITE "N18"; 250 | LOCATE COMP "sdram_d[4]" SITE "P18"; 251 | LOCATE COMP "sdram_d[5]" SITE "T18"; 252 | LOCATE COMP "sdram_d[6]" SITE "T17"; 253 | LOCATE COMP "sdram_d[7]" SITE "U20"; 254 | LOCATE COMP "sdram_d[8]" SITE "E19"; 255 | LOCATE COMP "sdram_d[9]" SITE "D20"; 256 | LOCATE COMP "sdram_d[10]" SITE "D19"; 257 | LOCATE COMP "sdram_d[11]" SITE "C20"; 258 | LOCATE COMP "sdram_d[12]" SITE "E18"; 259 | LOCATE COMP "sdram_d[13]" SITE "F18"; 260 | LOCATE COMP "sdram_d[14]" SITE "J18"; 261 | LOCATE COMP "sdram_d[15]" SITE "J17"; 262 | IOBUF PORT "sdram_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 263 | IOBUF PORT "sdram_cke" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 264 | IOBUF PORT "sdram_csn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 265 | IOBUF PORT "sdram_wen" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 266 | IOBUF PORT "sdram_rasn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 267 | IOBUF PORT "sdram_casn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 268 | IOBUF PORT "sdram_a[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 269 | IOBUF PORT "sdram_a[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 270 | IOBUF PORT "sdram_a[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 271 | IOBUF PORT "sdram_a[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 272 | IOBUF PORT "sdram_a[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 273 | IOBUF PORT "sdram_a[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 274 | IOBUF PORT "sdram_a[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 275 | IOBUF PORT "sdram_a[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 276 | IOBUF PORT "sdram_a[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 277 | IOBUF PORT "sdram_a[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 278 | IOBUF PORT "sdram_a[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 279 | IOBUF PORT "sdram_a[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 280 | IOBUF PORT "sdram_a[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 281 | IOBUF PORT "sdram_ba[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 282 | IOBUF PORT "sdram_ba[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 283 | IOBUF PORT "sdram_dqm[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 284 | IOBUF PORT "sdram_dqm[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 285 | IOBUF PORT "sdram_d[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 286 | IOBUF PORT "sdram_d[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 287 | IOBUF PORT "sdram_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 288 | IOBUF PORT "sdram_d[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 289 | IOBUF PORT "sdram_d[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 290 | IOBUF PORT "sdram_d[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 291 | IOBUF PORT "sdram_d[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 292 | IOBUF PORT "sdram_d[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 293 | IOBUF PORT "sdram_d[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 294 | IOBUF PORT "sdram_d[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 295 | IOBUF PORT "sdram_d[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 296 | IOBUF PORT "sdram_d[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 297 | IOBUF PORT "sdram_d[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 298 | IOBUF PORT "sdram_d[13]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 299 | IOBUF PORT "sdram_d[14]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 300 | IOBUF PORT "sdram_d[15]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 301 | 302 | # GPDI differential interface (Video) "gpdi" sheet 303 | LOCATE COMP "gpdi_dp[0]" SITE "A16"; # Blue + 304 | LOCATE COMP "gpdi_dn[0]" SITE "B16"; # Blue - 305 | LOCATE COMP "gpdi_dp[1]" SITE "A14"; # Green + 306 | LOCATE COMP "gpdi_dn[1]" SITE "C14"; # Green - 307 | LOCATE COMP "gpdi_dp[2]" SITE "A12"; # Red + 308 | LOCATE COMP "gpdi_dn[2]" SITE "A13"; # Red - 309 | LOCATE COMP "gpdi_dp[3]" SITE "A17"; # Clock + 310 | LOCATE COMP "gpdi_dn[3]" SITE "B18"; # Clock - 311 | LOCATE COMP "gpdi_ethp" SITE "A19"; # Ethernet + 312 | LOCATE COMP "gpdi_ethn" SITE "B20"; # Ethernet - 313 | LOCATE COMP "gpdi_cec" SITE "A18"; 314 | LOCATE COMP "gpdi_sda" SITE "B19"; # I2C shared with RTC 315 | LOCATE COMP "gpdi_scl" SITE "E12"; # I2C shared with RTC C12->E12 316 | IOBUF PORT "gpdi_dp[0]" IO_TYPE=LVCMOS33 DRIVE=4; 317 | IOBUF PORT "gpdi_dn[0]" IO_TYPE=LVCMOS33 DRIVE=4; 318 | IOBUF PORT "gpdi_dp[1]" IO_TYPE=LVCMOS33 DRIVE=4; 319 | IOBUF PORT "gpdi_dn[1]" IO_TYPE=LVCMOS33 DRIVE=4; 320 | IOBUF PORT "gpdi_dp[2]" IO_TYPE=LVCMOS33 DRIVE=4; 321 | IOBUF PORT "gpdi_dn[2]" IO_TYPE=LVCMOS33 DRIVE=4; 322 | IOBUF PORT "gpdi_dp[3]" IO_TYPE=LVCMOS33 DRIVE=4; 323 | IOBUF PORT "gpdi_dn[3]" IO_TYPE=LVCMOS33 DRIVE=4; 324 | IOBUF PORT "gpdi_ethp" IO_TYPE=LVCMOS33 DRIVE=4; 325 | IOBUF PORT "gpdi_ethn" IO_TYPE=LVCMOS33 DRIVE=4; 326 | IOBUF PORT "gpdi_cec" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 327 | IOBUF PORT "gpdi_sda" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 328 | IOBUF PORT "gpdi_scl" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 329 | 330 | # GPIO (default single-ended) "gpio", "ram", "gpdi" sheet 331 | # Pins enumerated gp[0-27], gn[0-27]. 332 | # With differential mode enabled on Lattice, 333 | # gp[] (+) are used, gn[] (-) are ignored from design 334 | # as they handle inverted signal by default. 335 | # To enable differential, rename LVCMOS33->LVCMOS33D 336 | LOCATE COMP "gp[0]" SITE "B11"; # J1_5+ GP0 337 | LOCATE COMP "gn[0]" SITE "C11"; # J1_5- GN0 338 | LOCATE COMP "gp[1]" SITE "A10"; # J1_7+ GP1 339 | LOCATE COMP "gn[1]" SITE "A11"; # J1_7- GN1 340 | LOCATE COMP "gp[2]" SITE "A9"; # J1_9+ GP2 341 | LOCATE COMP "gn[2]" SITE "B10"; # J1_9- GN2 342 | LOCATE COMP "gp[3]" SITE "B9"; # J1_11+ GP3 343 | LOCATE COMP "gn[3]" SITE "C10"; # J1_11- GN3 344 | LOCATE COMP "gp[4]" SITE "A7"; # J1_13+ GP4 345 | LOCATE COMP "gn[4]" SITE "A8"; # J1_13- GN4 346 | LOCATE COMP "gp[5]" SITE "C8"; # J1_15+ GP5 347 | LOCATE COMP "gn[5]" SITE "B8"; # J1_15- GN5 348 | LOCATE COMP "gp[6]" SITE "C6"; # J1_17+ GP6 349 | LOCATE COMP "gn[6]" SITE "C7"; # J1_17- GN6 350 | IOBUF PORT "gp[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 351 | IOBUF PORT "gn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 352 | IOBUF PORT "gp[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 353 | IOBUF PORT "gn[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 354 | IOBUF PORT "gp[2]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 355 | IOBUF PORT "gn[2]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 356 | IOBUF PORT "gp[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 357 | IOBUF PORT "gn[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 358 | IOBUF PORT "gp[4]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 359 | IOBUF PORT "gn[4]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 360 | IOBUF PORT "gp[5]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 361 | IOBUF PORT "gn[5]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 362 | IOBUF PORT "gp[6]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 363 | IOBUF PORT "gn[6]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 364 | LOCATE COMP "gp[7]" SITE "A6"; # J1_23+ GP7 365 | LOCATE COMP "gn[7]" SITE "B6"; # J1_23- GN7 366 | LOCATE COMP "gp[8]" SITE "A4"; # J1_25+ GP8 367 | LOCATE COMP "gn[8]" SITE "A5"; # J1_25- GN8 368 | LOCATE COMP "gp[9]" SITE "A2"; # J1_27+ GP9 369 | LOCATE COMP "gn[9]" SITE "B1"; # J1_27- GN9 370 | LOCATE COMP "gp[10]" SITE "C4"; # J1_29+ GP10 WIFI_GPIO27 371 | LOCATE COMP "gn[10]" SITE "B4"; # J1_29- GN10 372 | LOCATE COMP "gp[11]" SITE "F4"; # J1_31+ GP11 WIFI_GPIO25 373 | LOCATE COMP "gn[11]" SITE "E3"; # J1_31- GN11 WIFI_GPIO26 374 | LOCATE COMP "gp[12]" SITE "G3"; # J1_33+ GP12 WIFI_GPIO32 375 | LOCATE COMP "gn[12]" SITE "F3"; # J1_33- GN12 WIFI_GPIO33 376 | LOCATE COMP "gp[13]" SITE "H4"; # J1_35+ GP13 WIFI_GPIO34 377 | LOCATE COMP "gn[13]" SITE "G5"; # J1_35- GN13 WIFI_GPIO35 378 | IOBUF PORT "gp[7]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 379 | IOBUF PORT "gn[7]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 380 | IOBUF PORT "gp[8]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 381 | IOBUF PORT "gn[8]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 382 | IOBUF PORT "gp[9]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 383 | IOBUF PORT "gn[9]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 384 | IOBUF PORT "gp[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 385 | IOBUF PORT "gn[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 386 | IOBUF PORT "gp[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 387 | IOBUF PORT "gn[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 388 | IOBUF PORT "gp[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 389 | IOBUF PORT "gn[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 390 | IOBUF PORT "gp[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 391 | IOBUF PORT "gn[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 392 | LOCATE COMP "gp[14]" SITE "U18"; # J2_5+ GP14 393 | LOCATE COMP "gn[14]" SITE "U17"; # J2_5- GN14 394 | LOCATE COMP "gp[15]" SITE "N17"; # J2_7+ GP15 395 | LOCATE COMP "gn[15]" SITE "P16"; # J2_7- GN15 396 | LOCATE COMP "gp[16]" SITE "N16"; # J2_9+ GP16 397 | LOCATE COMP "gn[16]" SITE "M17"; # J2_9- GN16 398 | LOCATE COMP "gp[17]" SITE "L16"; # J2_11+ GP17 399 | LOCATE COMP "gn[17]" SITE "L17"; # J2_11- GN17 400 | LOCATE COMP "gp[18]" SITE "H18"; # J2_13+ GP18 401 | LOCATE COMP "gn[18]" SITE "H17"; # J2_13- GN18 402 | LOCATE COMP "gp[19]" SITE "F17"; # J2_15+ GP19 403 | LOCATE COMP "gn[19]" SITE "G18"; # J2_15- GN19 404 | LOCATE COMP "gp[20]" SITE "D18"; # J2_17+ GP20 405 | LOCATE COMP "gn[20]" SITE "E17"; # J2_17- GN20 406 | IOBUF PORT "gp[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 407 | IOBUF PORT "gn[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 408 | IOBUF PORT "gp[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 409 | IOBUF PORT "gn[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 410 | IOBUF PORT "gp[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 411 | IOBUF PORT "gn[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 412 | IOBUF PORT "gp[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 413 | IOBUF PORT "gn[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 414 | IOBUF PORT "gp[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 415 | IOBUF PORT "gn[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 416 | IOBUF PORT "gp[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 417 | IOBUF PORT "gn[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 418 | IOBUF PORT "gp[20]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 419 | IOBUF PORT "gn[20]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 420 | LOCATE COMP "gp[21]" SITE "C18"; # J2_23+ GP21 421 | LOCATE COMP "gn[21]" SITE "D17"; # J2_23- GN21 422 | LOCATE COMP "gp[22]" SITE "B15"; # J2_25+ GP22 423 | LOCATE COMP "gn[22]" SITE "C15"; # J2_25- GN22 424 | LOCATE COMP "gp[23]" SITE "B17"; # J2_27+ GP23 425 | LOCATE COMP "gn[23]" SITE "C17"; # J2_27- GN23 426 | LOCATE COMP "gp[24]" SITE "C16"; # J2_29+ GP24 427 | LOCATE COMP "gn[24]" SITE "D16"; # J2_29- GN24 428 | LOCATE COMP "gp[25]" SITE "D14"; # J2_31+ GP25 429 | LOCATE COMP "gn[25]" SITE "E14"; # J2_31- GN25 430 | LOCATE COMP "gp[26]" SITE "B13"; # J2_33+ GP26 431 | LOCATE COMP "gn[26]" SITE "C13"; # J2_33- GN26 432 | LOCATE COMP "gp[27]" SITE "D13"; # J2_35+ GP27 433 | LOCATE COMP "gn[27]" SITE "E13"; # J2_35- GN27 434 | IOBUF PORT "gp[21]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 435 | IOBUF PORT "gn[21]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 436 | IOBUF PORT "gp[22]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 437 | IOBUF PORT "gn[22]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 438 | IOBUF PORT "gp[23]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 439 | IOBUF PORT "gn[23]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 440 | IOBUF PORT "gp[24]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 441 | IOBUF PORT "gn[24]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 442 | IOBUF PORT "gp[25]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 443 | IOBUF PORT "gn[25]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 444 | IOBUF PORT "gp[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 445 | IOBUF PORT "gn[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 446 | IOBUF PORT "gp[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 447 | IOBUF PORT "gn[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 448 | 449 | ## PROGRAMN (reload bitstream from FLASH, exit from bootloader) 450 | # PCB v2.0.5 and higher 451 | LOCATE COMP "user_programn" SITE "M4"; 452 | IOBUF PORT "user_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 453 | 454 | ## SHUTDOWN "power", "ram" sheet (connected from PCB v1.7.5) 455 | # on PCB v1.7 shutdown is not connected to FPGA 456 | LOCATE COMP "shutdown" SITE "G16"; # FPGA receives 457 | IOBUF PORT "shutdown" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 458 | --------------------------------------------------------------------------------