├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── blackice ├── blackice.mk ├── blackice.pcf └── pll.v ├── data └── exp_lookup_table.rom ├── examples ├── midi │ ├── Makefile │ ├── README.md │ ├── apio.ini │ ├── envelope_generator_fixed_param.vh │ ├── midi_note_to_tone_freq.vh │ ├── midi_player.vh │ ├── midi_uart.vh │ ├── pins.pcf │ ├── simpleuart.vh │ ├── tone_generator_fixed_param.vh │ ├── top.v │ └── voice_fixed_param.vh ├── midi2 │ ├── Makefile │ ├── README.md │ ├── apio.ini │ ├── chip.txt │ ├── envelope_generator_fixed_param.vh │ ├── midi_note_to_tone_freq.vh │ ├── midi_player.vh │ ├── midi_uart.vh │ ├── pins.pcf │ ├── simpleuart.vh │ ├── top.v │ └── voice_fixed_param.vh ├── midi_voice_control │ ├── Makefile │ ├── README.md │ ├── apio.ini │ ├── f_table_44100Hz.mem │ ├── filter_tables.vh │ ├── gen_f_table.py │ ├── gen_q1_table.py │ ├── midi_note_to_tone_freq.vh │ ├── midi_player.vh │ ├── midi_uart.vh │ ├── pins.pcf │ ├── q1_table.mem │ ├── simpleuart.vh │ └── top.v ├── song_player │ ├── Makefile │ ├── README.md │ ├── apio.ini │ ├── example_song_bars.rom │ ├── example_song_pattern_map.rom │ ├── example_song_patterns.rom │ ├── pins.pcf │ ├── song_player.vh │ └── top.v └── triggered_adsr_voice │ ├── README.md │ ├── apio.ini │ ├── pins.pcf │ └── top.v ├── hdl ├── amplitude_modulator.vh ├── clock_divider.vh ├── eight_bit_exponential_decay_lookup.vh ├── envelope_generator.vh ├── filter_ewma.vh ├── filter_svf.vh ├── filter_svf_pipelined.vh ├── flanger.vh ├── multi_channel_mixer.vh ├── pdm_dac.vh ├── tiny-synth-all.vh ├── tone_generator.vh ├── tone_generator_noise.vh ├── tone_generator_pulse.vh ├── tone_generator_saw.vh ├── tone_generator_triangle.vh ├── two_into_one_mixer.vh └── voice.vh └── test ├── clock_divider ├── clock_divider.v └── clock_divider_tb.v ├── envelope_generator ├── envelope_generator.v └── envelope_generator_tb.v ├── filter_ewma ├── filter_ewma.v └── filter_ewma_tb.v ├── filter_svf ├── filter_svf.v └── filter_svf_tb.v └── filter_svf_pipelined ├── filter_svf.v └── filter_svf_tb.v /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig : https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | 12 | [*.{v,pcf}] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | # Tab indentation (no size specified) 17 | [Makefile] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dblite 2 | hardware.asc 3 | hardware.bin 4 | hardware.blif 5 | hardware.rpt 6 | *.out 7 | *.vcd 8 | *.idea 9 | **/*.iml 10 | *.bin 11 | *.asc 12 | *.rpt 13 | *.blif 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Tiny-synth is a an audio "synth" module written in verilog, for synthesis on an FPGA device such as the Lattice ICE40 series devices. 4 | 5 | Actually, it's a little more than just a synth - it's a set of building blocks for creating FPGA-based synth modules that you can choose to assemble in whichever way you choose. 6 | 7 | It's called tiny-synth because it's reasonably small and simple - at least it started out that way - but also because it's been developed on a TinyFPGA BX board, which is genuinely quite tiny! 8 | 9 | The synth in the top level verilog file (`top.v`) is loosely (okay, maybe not so loosely) based on the legendary MOS 6581 (SID) chip that provided audio to the Commodore 64. It was something I thought I'd try to replicate as an introductory project for a [TinyFPGA](http://tinyfpga.com) [BX](https://tinyfpga.com/bx/guide.html) board that I'd purchased. 10 | 11 | The demo project instantiates a module called a "`voice`"; The concept of a `voice` should be familiar to anyone who has looked at the SID chip. Each `voice` is made up of: 12 | 13 | - a tone generator (capable of generating saw, triangle, square/pulse and noise waveforms). 14 | - an ADSR envelope generator. 15 | - an amplitude modulator that modulates the amplitude of the tone with the envelope generator's output. 16 | 17 | "Analog" output is produced by a pulse-density modulator. This means that the output is actually a high-frequency square wave, and in order to see anything resembling analog output, you'll need to use at least a simple low-pass RC filter on the output pin(s). 18 | 19 | The below circuit works well enough for me, but YMMV. 20 | 21 | ``` 22 | e.g. 470 ohm eg. 10uF 23 | DOUT ---./\/\/\.---o------| |-------> "Analog" output 24 | | 25 | --- 26 | --- eg. 0.1uF 27 | | 28 | | 29 | --- GND 30 | - 31 | ``` 32 | 33 | _Note: the resistor value above was originally specified as 330ohm, but it seems like this may have been putting the 8mA rated IO's on the FPGA at risk. Especially if you happened to accidentally short-circuit the output. The new recommendation is 470ohm._ 34 | 35 | The module has been synthesized and bench-tested on a TinyFPGA BX board using the [IceStorm](http://www.clifford.at/icestorm/) toolchain. 36 | 37 | If you've got any problems or questions, please raise an issue against the project. 38 | 39 | If you'd like to contribute to the project, pull requests are welcomed! I'd especially love to see additional waveforms and something like a state-variable filter that can be wired into the audio path! 40 | 41 | If you use tiny-synth somewhere, or find this in any way useful please star the project and get in touch! 42 | 43 | ## Basic usage : TL;DR 44 | 45 | To get started quickly, all that you really need to do is instantiate a ```voice``` module, and wire it up to a ```pdm_dac``` module. Note the parameters below are constants, but you'll probably want a way of controlling/varying them for any kind of non-trivial use cases. 46 | 47 | ```verilog 48 | 49 | reg [11:0] voice_data; 50 | 51 | // instantiate a synthesizer voice 52 | voice voice( 53 | .clk(ONE_MHZ_CLK), 54 | .tone_freq(16'd16721), /* (16777216/16721) * 1000000Hz = 1kHz tone */ 55 | .waveform_enable(4'b0001), /* triangle tone generator */ 56 | .pulse_width(12'd0), /* pulse width only used for pulse waveform */ 57 | .rst(1'b0), /* force ReSeT pin low */ 58 | .en_ringmod(1'b0), /* disable ringmod */ 59 | .ringmod_source(1'b0), /* (source connection for ringmod; forced to zero) */ 60 | .en_sync(1'b0), /* disable oscillator synchronization */ 61 | .sync_source(1'b0), /* (source connection for sync; forced to zero) */ 62 | .gate(SLOW_CLK), /* gate on triggers attack; gate off triggers decay */ 63 | .attack(4'b0001), /* attack rate (8ms) */ 64 | .decay(4'b0001), /* decay rate (6ms)*/ 65 | .sustain(4'b1000), /* sustain level (~1/2 volume)*/ 66 | .rel(4'b0010) /* release rate (48ms) */ 67 | .dout(voice_data), /* route output to voice_data register */ 68 | ); 69 | 70 | // instantiate a DAC to take voice output and route it to an external pin with PDM modulation 71 | pdm_dac #(.DATA_BITS(12)) dac( 72 | .din(voice_data), 73 | .clk(CLK), // DAC clock 74 | .dout(PIN_1) // route audio to PIN 1 75 | ); 76 | ``` 77 | 78 | ## Examples 79 | 80 | The `/examples` folder contains a number of examples which give ideas for composing the various elements in tiny-synth into higher-level abstractions. 81 | 82 | # Modules 83 | 84 | ## `voice` 85 | 86 | ### Overview 87 | 88 | Each voice has a 24-bit phase-accumulator which is used to generate the output 89 | waveforms. 90 | 91 | It also has an ADSR envelope generator. 92 | 93 | The envelope generator uses a linear attack, and an exponential fall-off function for the 94 | decay and release cycles. 95 | 96 | ### Parameters 97 | 98 | | parameter | default | description | 99 | | --- | --- | --- | 100 | | OUTPUT_BITS | 12 | bit-width of PCM output register | 101 | | FREQ_BITS | 16 | number of bits to use for `tone_freq` input | 102 | | PULSEWIDTH_BITS | 12 | number of bits resolution for `pulse_width` register | 103 | | ACCUMULATOR_BITS | 24 | number of bits resolution for frequency accumulator | 104 | 105 | 106 | ### IO 107 | 108 | | parameter | description | 109 | | --- | --- | 110 | | `clk:1` | reference clock for the tone generator | 111 | | `tone_freq:16` | Value to add to the phase-accumulator every `clk` cycle. When the accumulator overflows, the next output cycle starts.

`Fout (Hz)` = (`tone_freq` x `clk`) / 16777216

`tone_freq` = (`Fout (Hz)` x 16777216) / `clk` | 112 | | `waveform_enable:4` | `0001` = triangle /\\/\\/\\
`0010` = saw /|/|/|
`0100` = pulse _-_-_-_
`1000` = LFSR noise (random).

If multiple waveforms are selected, the outputs are logically ANDed together. | 113 | | `pulse_width:12` | when pulse waveform is selected, if accumulator < `pulse_width`, output is high; else low | 114 | | `rst:1` | reset; when high, the accumulator is reset; as is the ADSR envelope generator. | 115 | | `en_ringmod:1` | enable the ring modulator (only has any effect when the triangle wave is selected) | 116 | | `ringmod_source:1` | source of modulation; should be the MSB from another voice's accumulator; this triggers the inversion of this voice's triangle waveform (as opposed to using it's own MSB). | 117 | | `accumulator_msb:1` | most significant bit of accumulator - used to feed ringmod source of another oscillator. | 118 | | `accumulator_overflow:1` | true when the value in this accumulator has wrapped past zero; used to sync with another oscillator. | 119 | | `en_sync:1` | enable synchronizing this oscillator with sync_source. if true, this oscillator will reset to 0 whenever sync_source is set. | 120 | | `sync_source:1` | when this is set to one, the oscillator's accumulator will be reset to 0. Normally fed from another oscillator to enable sync effects. | 121 | | `gate` | A rising gate will trigger the ADSR cycle to begin, according to the timings in the `attack`/`decay`/`sustain`/`release` parameters. A falling gate will trigger the release cycle. | 122 | | `attack:4` | How long it takes the envelope generator attack cycle to go from zero volume to full-scale.

`0000` = 2ms
`0001` = 8ms
`0010` = 16ms
`0011` = 24ms
`0100` = 38ms
`0101` = 56ms
`0110` = 68ms
`0111` = 80ms
`1000` = 100ms
`1001` = 250ms
`1010` = 500ms
`1011` = 800ms
`1100` = 1 second
`1101` = 3 seconds
`1110` = 5 seconds
`1111` = 8 seconds| 123 | | `decay:4` | How long it takes the envelope generator to decay from full-scale after attack to the sustain level.

`0000` = 6ms
`0001` = 24ms
`0010` = 48ms
`0011` = 72ms
`0100` = 114ms
`0101` = 168ms
`0110` = 204ms
`0111` = 240ms
`1000` = 300ms
`1001` = 750ms
`1010` = 1.5 seconds
`1011` = 2.4 seconds
`1100` = 3 seconds
`1101` = 9 seconds
`1110` = 15 seconds
`1111` = 24 seconds| 124 | | `sustain:4` | The level that the note will be sustained at while the 'gate' is still enabled. Values range from zero (off; ie. note will decay to zero without a sustain phase) to 15 (max). | 125 | | `rel:4` | How long it takes the envelope generator to fall from the sustain level to zero once the gate has been switched off.

`0000` = 6ms
`0001` = 24ms
`0010` = 48ms
`0011` = 72ms
`0100` = 114ms
`0101` = 168ms
`0110` = 204ms
`0111` = 240ms
`1000` = 300ms
`1001` = 750ms
`1010` = 1.5 seconds
`1011` = 2.4 seconds
`1100` = 3 seconds
`1101` = 9 seconds
`1110` = 15 seconds
`1111` = 24 seconds | 126 | | `dout:12` | digital sample output; a new sample is generated every CLK cycle. | 127 | 128 | ## pdm_dac 129 | 130 | | parameter | description | 131 | | --- | --- | 132 | | `din:12` | Input data samples | 133 | | `clk:1` | High speed clock for output | 134 | | `dout:1` | Pulse-density modulated output | 135 | -------------------------------------------------------------------------------- /blackice/blackice.mk: -------------------------------------------------------------------------------- 1 | chip.bin: $(VERILOG_FILES) ${PCF_FILE} 2 | yosys -q -f "verilog -Dblackice" -p "synth_ice40 -blif chip.blif" $(VERILOG_FILES) 3 | arachne-pnr -d 8k -P tq144:4k -p ${PCF_FILE} chip.blif -o chip.txt 4 | icepack chip.txt chip.bin 5 | 6 | .PHONY: upload 7 | upload: chip.bin 8 | stty -F /dev/ttyACM1 raw 9 | cat chip.bin >/dev/ttyACM1 10 | 11 | .PHONY: clean 12 | clean: 13 | $(RM) -f chip.blif chip.txt chip.bin 14 | -------------------------------------------------------------------------------- /blackice/blackice.pcf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # TinyFPGA BX constraint file (.pcf) 4 | # 5 | ############################################################################### 6 | # 7 | # Copyright (c) 2018, Luke Valenty 8 | # All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | # 13 | # 1. Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | # 2. Redistributions in binary form must reproduce the above copyright notice, 16 | # this list of conditions and the following disclaimer in the documentation 17 | # and/or other materials provided with the distribution. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | # 30 | # The views and conclusions contained in the software and documentation are those 31 | # of the authors and should not be interpreted as representing official policies, 32 | # either expressed or implied, of the project. 33 | # 34 | ############################################################################### 35 | 36 | #### 37 | # TinyFPGA BX information: https://github.com/tinyfpga/TinyFPGA-BX/ 38 | #### 39 | 40 | # Left side of board 41 | set_io --warn-no-port PIN_1 34 42 | #set_io --warn-no-port PIN_2 A1 43 | #set_io --warn-no-port PIN_3 B1 44 | #set_io --warn-no-port PIN_4 C2 45 | #set_io --warn-no-port PIN_5 C1 46 | #set_io --warn-no-port PIN_6 D2 47 | #set_io --warn-no-port PIN_7 D1 48 | #set_io --warn-no-port PIN_8 E2 49 | #set_io --warn-no-port PIN_9 E1 50 | #set_io --warn-no-port PIN_10 G2 51 | #set_io --warn-no-port PIN_11 H1 52 | #set_io --warn-no-port PIN_12 J1 53 | #set_io --warn-no-port PIN_13 H2 54 | 55 | # Right side of board 56 | set_io --warn-no-port PIN_14 20 57 | set_io --warn-no-port PIN_15 19 58 | #set_io --warn-no-port PIN_16 D8 59 | #set_io --warn-no-port PIN_17 C9 60 | #set_io --warn-no-port PIN_18 A9 61 | #set_io --warn-no-port PIN_19 B8 62 | #set_io --warn-no-port PIN_20 A8 63 | #set_io --warn-no-port PIN_21 B7 64 | #set_io --warn-no-port PIN_22 A7 65 | #set_io --warn-no-port PIN_23 B6 66 | #set_io --warn-no-port PIN_24 A6 67 | 68 | # SPI flash interface on bottom of board 69 | #set_io --warn-no-port SPI_SS F7 70 | #set_io --warn-no-port SPI_SCK G7 71 | #set_io --warn-no-port SPI_IO0 G6 72 | #set_io --warn-no-port SPI_IO1 H7 73 | #set_io --warn-no-port SPI_IO2 H4 74 | #set_io --warn-no-port SPI_IO3 J8 75 | 76 | # General purpose pins on bottom of board 77 | #set_io --warn-no-port PIN_25 G1 78 | #set_io --warn-no-port PIN_26 J3 79 | #set_io --warn-no-port PIN_27 J4 80 | #set_io --warn-no-port PIN_28 G9 81 | #set_io --warn-no-port PIN_29 J9 82 | #set_io --warn-no-port PIN_30 E8 83 | #set_io --warn-no-port PIN_31 J2 84 | 85 | # LED 86 | set_io --warn-no-port LED 71 87 | 88 | # USB 89 | #set_io --warn-no-port USBP B4 90 | #set_io --warn-no-port USBN A4 91 | set_io --warn-no-port USBPU 33 92 | 93 | # 16MHz clock 94 | set_io --warn-no-port CLK_100 129 # input 95 | 96 | set_io SWITCHES[0] 37 97 | set_io SWITCHES[1] 38 98 | set_io SWITCHES[2] 39 99 | set_io SWITCHES[3] 41 100 | 101 | -------------------------------------------------------------------------------- /blackice/pll.v: -------------------------------------------------------------------------------- 1 | /** 2 | * PLL configuration 3 | * 4 | * This Verilog module was generated automatically 5 | * using the icepll tool from the IceStorm project. 6 | * Use at your own risk. 7 | * 8 | * Given input frequency: 100.000 MHz 9 | * Requested output frequency: 16.000 MHz 10 | * Achieved output frequency: 16.016 MHz 11 | */ 12 | 13 | module pll( 14 | input clock_in, 15 | output clock_out, 16 | output locked 17 | ); 18 | 19 | SB_PLL40_CORE #( 20 | .FEEDBACK_PATH("SIMPLE"), 21 | .DIVR(4'b0011), // DIVR = 3 22 | .DIVF(7'b0101000), // DIVF = 40 23 | .DIVQ(3'b110), // DIVQ = 6 24 | .FILTER_RANGE(3'b010) // FILTER_RANGE = 2 25 | ) uut ( 26 | .LOCK(locked), 27 | .RESETB(1'b1), 28 | .BYPASS(1'b0), 29 | .REFERENCECLK(clock_in), 30 | .PLLOUTCORE(clock_out) 31 | ); 32 | 33 | endmodule 34 | -------------------------------------------------------------------------------- /data/exp_lookup_table.rom: -------------------------------------------------------------------------------- 1 | // for reference this table was generated by: 2 | // 3 | // din <= [ 0, 1, ..., 255 ] 4 | // dout <= exp(-din/45.9)*255 5 | // 6 | // (The magic number 45.9 was chosen to ensure that output hitting zero was 7 | // kept as close as possible to when the input hit full-scale 8 | FF 9 | F9 10 | F4 11 | EE 12 | E9 13 | E4 14 | DF 15 | DA 16 | D6 17 | D1 18 | CD 19 | C8 20 | C4 21 | C0 22 | BB 23 | B7 24 | B3 25 | B0 26 | AC 27 | A8 28 | A4 29 | A1 30 | 9D 31 | 9A 32 | 97 33 | 93 34 | 90 35 | 8D 36 | 8A 37 | 87 38 | 84 39 | 81 40 | 7E 41 | 7C 42 | 79 43 | 76 44 | 74 45 | 71 46 | 6F 47 | 6D 48 | 6A 49 | 68 50 | 66 51 | 63 52 | 61 53 | 5F 54 | 5D 55 | 5B 56 | 59 57 | 57 58 | 55 59 | 53 60 | 52 61 | 50 62 | 4E 63 | 4C 64 | 4B 65 | 49 66 | 48 67 | 46 68 | 44 69 | 43 70 | 42 71 | 40 72 | 3F 73 | 3D 74 | 3C 75 | 3B 76 | 39 77 | 38 78 | 37 79 | 36 80 | 35 81 | 33 82 | 32 83 | 31 84 | 30 85 | 2F 86 | 2E 87 | 2D 88 | 2C 89 | 2B 90 | 2A 91 | 29 92 | 28 93 | 28 94 | 27 95 | 26 96 | 25 97 | 24 98 | 23 99 | 23 100 | 22 101 | 21 102 | 20 103 | 20 104 | 1F 105 | 1E 106 | 1E 107 | 1D 108 | 1C 109 | 1C 110 | 1B 111 | 1B 112 | 1A 113 | 19 114 | 19 115 | 18 116 | 18 117 | 17 118 | 17 119 | 16 120 | 16 121 | 15 122 | 15 123 | 14 124 | 14 125 | 13 126 | 13 127 | 13 128 | 12 129 | 12 130 | 11 131 | 11 132 | 11 133 | 10 134 | 10 135 | 10 136 | 0F 137 | 0F 138 | 0F 139 | 0E 140 | 0E 141 | 0E 142 | 0D 143 | 0D 144 | 0D 145 | 0C 146 | 0C 147 | 0C 148 | 0C 149 | 0B 150 | 0B 151 | 0B 152 | 0B 153 | 0A 154 | 0A 155 | 0A 156 | 0A 157 | 09 158 | 09 159 | 09 160 | 09 161 | 09 162 | 08 163 | 08 164 | 08 165 | 08 166 | 08 167 | 07 168 | 07 169 | 07 170 | 07 171 | 07 172 | 07 173 | 07 174 | 06 175 | 06 176 | 06 177 | 06 178 | 06 179 | 06 180 | 06 181 | 05 182 | 05 183 | 05 184 | 05 185 | 05 186 | 05 187 | 05 188 | 05 189 | 04 190 | 04 191 | 04 192 | 04 193 | 04 194 | 04 195 | 04 196 | 04 197 | 04 198 | 04 199 | 03 200 | 03 201 | 03 202 | 03 203 | 03 204 | 03 205 | 03 206 | 03 207 | 03 208 | 03 209 | 03 210 | 03 211 | 03 212 | 02 213 | 02 214 | 02 215 | 02 216 | 02 217 | 02 218 | 02 219 | 02 220 | 02 221 | 02 222 | 02 223 | 02 224 | 02 225 | 02 226 | 02 227 | 02 228 | 02 229 | 02 230 | 02 231 | 01 232 | 01 233 | 01 234 | 01 235 | 01 236 | 01 237 | 01 238 | 01 239 | 01 240 | 01 241 | 01 242 | 01 243 | 01 244 | 01 245 | 01 246 | 01 247 | 01 248 | 01 249 | 01 250 | 01 251 | 01 252 | 01 253 | 01 254 | 01 255 | 01 256 | 01 257 | 01 258 | 01 259 | 01 260 | 01 261 | 01 262 | 01 263 | 00 264 | -------------------------------------------------------------------------------- /examples/midi/Makefile: -------------------------------------------------------------------------------- 1 | VERILOG_FILES=top.v ../../blackice//pll.v 2 | PCF_FILE = ../../blackice/blackice.pcf 3 | 4 | include ../../blackice/blackice.mk 5 | -------------------------------------------------------------------------------- /examples/midi/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A simple MIDI-based example for tiny-synth. 4 | 5 | This example is an eight-voice MIDI-connected synthesizer. 6 | 7 | ## Principle of operation 8 | 9 | The MIDI interface uses Clifford Wolf's simpleuart code (slightly modified so 10 | that it could be used outside of the PicoSoC environment) to read data from the 11 | MIDI interface. 12 | 13 | `simpleuart` is wrapped with the `midi_uart` module, which essentially waits for 14 | whole MIDI frames (command+parameter messages) to arrive before passing them 15 | up to the next layer. 16 | 17 | `midi_player` instantiates 8 voices, and keeps track which voices are available, 18 | and which are busy playing. When a "note-on" MIDI message arrives, the first 19 | available voice is chosen, and the voice is assigned / gated appropriately. 20 | "note-off" messages simply find all voices playing the correct note, and 21 | gate them off. 22 | 23 | # Before you start 24 | 25 | This example requires a bit of extra circuitry - namely MIDI input requires a H11L1 26 | opto-coupler to isolate your instrument, and to help prevent ground loops as per 27 | the MIDI specification. 28 | 29 | ## Audio output 30 | 31 | As with the other tiny-synth examples, audio-out requires some analog circuitry: 32 | 33 | 34 | The below circuit can be used to filter the output from `PIN_1`. 35 | 36 | ``` 37 | e.g. 330 ohm eg. 10uF 38 | PIN_1 >---./\/\/\.---o------| |-------> Analog out >-- 39 | | 40 | --- 41 | --- eg. 0.1uF 42 | | 43 | | 44 | --- GND 45 | - 46 | ``` 47 | 48 | ## MIDI input 49 | 50 | MIDI input is via a 5-pin DIN MIDI connector (I used a breadboard-friendly one), 51 | and an H11L1 opto-coupler, with a few passives. 52 | 53 | The circuit I used is described below. 54 | 55 | ### MIDI connector pinout 56 | 57 | The pins in the MIDI connector are numbered as below. Imagine this is a female 58 | DIN socket, and you're looking at it head-on. 59 | 60 | ``` 61 | 62 | 5-PIN MIDI female socket; looking head-on 63 | 64 | - '|_|' - 65 | ' ' 66 | | 1 3 | 67 | . 4 5 . 68 | . 2 . 69 | `-----' 70 | 71 | ``` 72 | 73 | ### Circuit diagram 74 | 75 | ``` 76 | ___ GND 77 | | 78 | H11L1 --- 1uF 79 | _____________________ --- 80 | 220ohm 1 | | 6 | 81 | MIDI_IN PIN4 -------./\/\.----o-----------|---. ----.o--|-------o----------o-----------> 3v3 82 | _|_ | _|_ | . | 4 \ 83 | 1N4148 /_\ | \ / '' | \=\ |o-|----------. / 270ohm 84 | | 2 | ~~~ | . | 5 | \ 85 | MIDI_IN PIN5 -----------------o-----------|---' ----'o--|----. `-------o-----------> UART RX (PIN 14 on TinyFPGA BX) 86 | |___________________| | 87 | --- GND 88 | ``` 89 | -------------------------------------------------------------------------------- /examples/midi/apio.ini: -------------------------------------------------------------------------------- 1 | [env] 2 | board = TinyFPGA-BX 3 | 4 | -------------------------------------------------------------------------------- /examples/midi/envelope_generator_fixed_param.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_ENV_GENERATOR_FIXED_PARAM__ 2 | `define __TINY_SYNTH_ENV_GENERATOR_FIXED_PARAM__ 3 | 4 | `include "../../hdl/eight_bit_exponential_decay_lookup.vh" 5 | 6 | /* =================== 7 | * Envelope generator 8 | * =================== 9 | * 10 | * Creates an 8-bit ADSR (attack, decay, sustain, release) volume envelope. 11 | * 12 | * .. 13 | * A . `. D S 14 | * . `---------- 15 | * . . R 16 | * . ` . 17 | * ----------------------------> 18 | * t 19 | * 20 | * By modulating the tone generator output with an ADSR envelope like this, 21 | * it's possible to create many interesting sounds. 22 | * 23 | * The input parameters are described in README.md. 24 | * 25 | * Principle of operation: 26 | * 27 | * The envelope generator is a state machine that makes use of an accumulator for 28 | * generation of the output wave shape & timing. For each of the A/D/R stages, 29 | * the state is advanced when the accumulator overflows. 30 | * 31 | * The envelope is 'triggered' by a gate signal, and as long as gate is held 32 | * high, the envelope won't transition past the sustain phase. When gate is 33 | * released, the envelope will transition into the release phase. 34 | * 35 | * The decay and release phases use an exponential fall-off. 36 | */ 37 | module envelope_generator_fixed_param #( 38 | parameter SAMPLE_CLK_FREQ = 44100, 39 | parameter ACCUMULATOR_BITS = 26, 40 | parameter ATTACK_INC = 1000, 41 | parameter DECAY_INC = 1000, 42 | parameter [7:0] SUSTAIN_VOLUME = 8'd128, 43 | parameter RELEASE_INC = 1000 44 | ) 45 | ( 46 | input clk, 47 | input gate, 48 | output is_idle, 49 | output reg [7:0] amplitude, 50 | input rst); 51 | 52 | localparam ACCUMULATOR_SIZE = 2**ACCUMULATOR_BITS; 53 | localparam ACCUMULATOR_MAX = ACCUMULATOR_SIZE-1; 54 | 55 | reg [ACCUMULATOR_BITS:0] accumulator; 56 | reg [16:0] accumulator_inc; /* value to add to accumulator */ 57 | 58 | // calculate the amount to add to the accumulator each clock cycle to 59 | // achieve a full-scale value in n number of seconds. (n can be fractional seconds) 60 | `define CALCULATE_PHASE_INCREMENT(n) $rtoi(ACCUMULATOR_SIZE / ($itor(n) * SAMPLE_CLK_FREQ)) 61 | 62 | // localparam ATTACK_INC = `CALCULATE_PHASE_INCREMENT(ATTACK_SECONDS); 63 | // localparam DECAY_INC = `CALCULATE_PHASE_INCREMENT(DECAY_SECONDS); 64 | // localparam RELEASE_INC = `CALCULATE_PHASE_INCREMENT(RELEASE_SECONDS); 65 | localparam [7:0] SUSTAIN_GAP = 255 - SUSTAIN_VOLUME; 66 | 67 | // Envelope states 68 | localparam OFF = 3'd0; 69 | localparam ATTACK = 3'd1; 70 | localparam DECAY = 3'd2; 71 | localparam SUSTAIN = 3'd3; 72 | localparam RELEASE = 3'd4; 73 | 74 | reg[2:0] state; 75 | 76 | assign is_idle = (state == OFF); 77 | 78 | initial begin 79 | state = OFF; 80 | amplitude = 0; 81 | accumulator = 0; 82 | end 83 | 84 | reg [16:0] dectmp; /* scratch-register for intermediate result of decay scaling */ 85 | reg [16:0] reltmp; /* scratch-register for intermediate-result of release-scaling */ 86 | 87 | wire [7:0] exp_out; // exponential decay mapping of accumulator output; used for decay and release cycles 88 | eight_bit_exponential_decay_lookup exp_lookup(.din(accumulator[ACCUMULATOR_BITS-1 -: 8]), .dout(exp_out)); 89 | 90 | /* calculate the next state of the envelope generator based on 91 | the state that we've just moved past, and the gate signal */ 92 | function [2:0] next_state; 93 | input [2:0] s; 94 | input g; 95 | begin 96 | case ({ s, g }) 97 | { ATTACK, 1'b0 }: next_state = RELEASE; /* attack, gate off => skip decay, sustain; go to release */ 98 | { ATTACK, 1'b1 }: next_state = DECAY; /* attack, gate still on => decay */ 99 | { DECAY, 1'b0 }: next_state = RELEASE; /* decay, gate off => skip sustain; go to release */ 100 | { DECAY, 1'b1 }: next_state = SUSTAIN; /* decay, gate still on => sustain */ 101 | { SUSTAIN, 1'b0 }: next_state = RELEASE; /* sustain, gate off => go to release */ 102 | { SUSTAIN, 1'b1 }: next_state = SUSTAIN; /* sustain, gate on => stay in sustain */ 103 | { RELEASE, 1'b0 }: next_state = OFF; /* release, gate off => end state */ 104 | { RELEASE, 1'b1 }: next_state = ATTACK; /* release, gate on => attack */ 105 | { OFF, 1'b0 }: next_state = OFF; /* end_state, gate off => stay in end state */ 106 | { OFF, 1'b1 }: next_state = ATTACK; /* end_state, gate on => attack */ 107 | default: next_state = OFF; /* default is end (off) state */ 108 | endcase 109 | end 110 | endfunction 111 | 112 | wire overflow; 113 | assign overflow = accumulator[ACCUMULATOR_BITS]; 114 | 115 | reg prev_gate; 116 | 117 | always @(posedge clk) 118 | begin 119 | 120 | /* check for gate low->high transitions (straight to attack phase)*/ 121 | prev_gate <= gate; 122 | if (gate && !prev_gate) 123 | begin 124 | accumulator <= 0; 125 | state <= ATTACK; 126 | end 127 | 128 | /* otherwise, flow through ADSR state machine */ 129 | if (overflow) 130 | begin 131 | accumulator <= 0; 132 | dectmp <= 8'd255; 133 | state <= next_state(state, gate); 134 | end 135 | else begin 136 | case (state) 137 | ATTACK: 138 | begin 139 | accumulator <= accumulator + ATTACK_INC; 140 | amplitude <= accumulator[ACCUMULATOR_BITS-1 -: 8]; 141 | end 142 | DECAY: 143 | begin 144 | accumulator <= accumulator + DECAY_INC; 145 | dectmp <= ((exp_out * SUSTAIN_GAP) >> 8) + SUSTAIN_VOLUME; 146 | amplitude <= dectmp; 147 | end 148 | SUSTAIN: 149 | begin 150 | amplitude <= SUSTAIN_VOLUME; 151 | state <= next_state(state, gate); 152 | end 153 | RELEASE: 154 | begin 155 | accumulator <= accumulator + RELEASE_INC; 156 | reltmp <= ((exp_out * SUSTAIN_VOLUME) >> 8); 157 | amplitude <= reltmp; 158 | if (gate) begin 159 | amplitude <= 0; 160 | accumulator <= 0; 161 | state <= next_state(state, gate); 162 | end 163 | end 164 | default: 165 | begin 166 | amplitude <= 0; 167 | accumulator <= 0; 168 | state <= next_state(state, gate); 169 | end 170 | endcase 171 | end 172 | end 173 | endmodule 174 | 175 | `endif 176 | -------------------------------------------------------------------------------- /examples/midi/midi_note_to_tone_freq.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MIDI_NOTE_TO_TONE_FREQ__ 2 | `define __TINY_SYNTH_MIDI_NOTE_TO_TONE_FREQ__ 3 | 4 | function [15:0] midi_note_to_tone_freq 5 | ( 6 | input [7:0] midi_note 7 | ); 8 | 9 | case (midi_note) 10 | 8'h00: midi_note_to_tone_freq = 16'h0089; 11 | 8'h01: midi_note_to_tone_freq = 16'h0091; 12 | 8'h02: midi_note_to_tone_freq = 16'h0099; 13 | 8'h03: midi_note_to_tone_freq = 16'h00a3; 14 | 8'h04: midi_note_to_tone_freq = 16'h00ac; 15 | 8'h05: midi_note_to_tone_freq = 16'h00b7; 16 | 8'h06: midi_note_to_tone_freq = 16'h00c1; 17 | 8'h07: midi_note_to_tone_freq = 16'h00cd; 18 | 8'h08: midi_note_to_tone_freq = 16'h00d9; 19 | 8'h09: midi_note_to_tone_freq = 16'h00e6; 20 | 8'h0a: midi_note_to_tone_freq = 16'h00f4; 21 | 8'h0b: midi_note_to_tone_freq = 16'h0102; 22 | 8'h0c: midi_note_to_tone_freq = 16'h0112; 23 | 8'h0d: midi_note_to_tone_freq = 16'h0122; 24 | 8'h0e: midi_note_to_tone_freq = 16'h0133; 25 | 8'h0f: midi_note_to_tone_freq = 16'h0146; 26 | 8'h10: midi_note_to_tone_freq = 16'h0159; 27 | 8'h11: midi_note_to_tone_freq = 16'h016e; 28 | 8'h12: midi_note_to_tone_freq = 16'h0183; 29 | 8'h13: midi_note_to_tone_freq = 16'h019b; 30 | 8'h14: midi_note_to_tone_freq = 16'h01b3; 31 | 8'h15: midi_note_to_tone_freq = 16'h01cd; 32 | 8'h16: midi_note_to_tone_freq = 16'h01e8; 33 | 8'h17: midi_note_to_tone_freq = 16'h0205; 34 | 8'h18: midi_note_to_tone_freq = 16'h0224; 35 | 8'h19: midi_note_to_tone_freq = 16'h0245; 36 | 8'h1a: midi_note_to_tone_freq = 16'h0267; 37 | 8'h1b: midi_note_to_tone_freq = 16'h028c; 38 | 8'h1c: midi_note_to_tone_freq = 16'h02b3; 39 | 8'h1d: midi_note_to_tone_freq = 16'h02dc; 40 | 8'h1e: midi_note_to_tone_freq = 16'h0307; 41 | 8'h1f: midi_note_to_tone_freq = 16'h0336; 42 | 8'h20: midi_note_to_tone_freq = 16'h0366; 43 | 8'h21: midi_note_to_tone_freq = 16'h039a; 44 | 8'h22: midi_note_to_tone_freq = 16'h03d1; 45 | 8'h23: midi_note_to_tone_freq = 16'h040b; 46 | 8'h24: midi_note_to_tone_freq = 16'h0449; 47 | 8'h25: midi_note_to_tone_freq = 16'h048a; 48 | 8'h26: midi_note_to_tone_freq = 16'h04cf; 49 | 8'h27: midi_note_to_tone_freq = 16'h0518; 50 | 8'h28: midi_note_to_tone_freq = 16'h0566; 51 | 8'h29: midi_note_to_tone_freq = 16'h05b8; 52 | 8'h2a: midi_note_to_tone_freq = 16'h060f; 53 | 8'h2b: midi_note_to_tone_freq = 16'h066c; 54 | 8'h2c: midi_note_to_tone_freq = 16'h06cd; 55 | 8'h2d: midi_note_to_tone_freq = 16'h0735; 56 | 8'h2e: midi_note_to_tone_freq = 16'h07a3; 57 | 8'h2f: midi_note_to_tone_freq = 16'h0817; 58 | 8'h30: midi_note_to_tone_freq = 16'h0892; 59 | 8'h31: midi_note_to_tone_freq = 16'h0915; 60 | 8'h32: midi_note_to_tone_freq = 16'h099f; 61 | 8'h33: midi_note_to_tone_freq = 16'h0a31; 62 | 8'h34: midi_note_to_tone_freq = 16'h0acd; 63 | 8'h35: midi_note_to_tone_freq = 16'h0b71; 64 | 8'h36: midi_note_to_tone_freq = 16'h0c1f; 65 | 8'h37: midi_note_to_tone_freq = 16'h0cd8; 66 | 8'h38: midi_note_to_tone_freq = 16'h0d9b; 67 | 8'h39: midi_note_to_tone_freq = 16'h0e6a; 68 | 8'h3a: midi_note_to_tone_freq = 16'h0f46; 69 | 8'h3b: midi_note_to_tone_freq = 16'h102e; 70 | 8'h3c: midi_note_to_tone_freq = 16'h1125; /* middle C */ 71 | 8'h3d: midi_note_to_tone_freq = 16'h122a; 72 | 8'h3e: midi_note_to_tone_freq = 16'h133e; 73 | 8'h3f: midi_note_to_tone_freq = 16'h1463; 74 | 8'h40: midi_note_to_tone_freq = 16'h159a; 75 | 8'h41: midi_note_to_tone_freq = 16'h16e2; 76 | 8'h42: midi_note_to_tone_freq = 16'h183f; 77 | 8'h43: midi_note_to_tone_freq = 16'h19b0; 78 | 8'h44: midi_note_to_tone_freq = 16'h1b37; 79 | 8'h45: midi_note_to_tone_freq = 16'h1cd5; 80 | 8'h46: midi_note_to_tone_freq = 16'h1e8c; 81 | 8'h47: midi_note_to_tone_freq = 16'h205d; 82 | 8'h48: midi_note_to_tone_freq = 16'h224a; 83 | 8'h49: midi_note_to_tone_freq = 16'h2454; 84 | 8'h4a: midi_note_to_tone_freq = 16'h267d; 85 | 8'h4b: midi_note_to_tone_freq = 16'h28c7; 86 | 8'h4c: midi_note_to_tone_freq = 16'h2b34; 87 | 8'h4d: midi_note_to_tone_freq = 16'h2dc5; 88 | 8'h4e: midi_note_to_tone_freq = 16'h307e; 89 | 8'h4f: midi_note_to_tone_freq = 16'h3360; 90 | 8'h50: midi_note_to_tone_freq = 16'h366f; 91 | 8'h51: midi_note_to_tone_freq = 16'h39ab; 92 | 8'h52: midi_note_to_tone_freq = 16'h3d19; 93 | 8'h53: midi_note_to_tone_freq = 16'h40bb; 94 | 8'h54: midi_note_to_tone_freq = 16'h4495; 95 | 8'h55: midi_note_to_tone_freq = 16'h48a8; 96 | 8'h56: midi_note_to_tone_freq = 16'h4cfb; 97 | 8'h57: midi_note_to_tone_freq = 16'h518e; 98 | 8'h58: midi_note_to_tone_freq = 16'h5668; 99 | 8'h59: midi_note_to_tone_freq = 16'h5b8b; 100 | 8'h5a: midi_note_to_tone_freq = 16'h60fd; 101 | 8'h5b: midi_note_to_tone_freq = 16'h66c1; 102 | 8'h5c: midi_note_to_tone_freq = 16'h6cde; 103 | 8'h5d: midi_note_to_tone_freq = 16'h7357; 104 | 8'h5e: midi_note_to_tone_freq = 16'h7a33; 105 | 8'h5f: midi_note_to_tone_freq = 16'h8177; 106 | 8'h60: midi_note_to_tone_freq = 16'h892a; 107 | 8'h61: midi_note_to_tone_freq = 16'h9151; 108 | 8'h62: midi_note_to_tone_freq = 16'h99f6; 109 | 8'h63: midi_note_to_tone_freq = 16'ha31d; 110 | 8'h64: midi_note_to_tone_freq = 16'hacd0; 111 | 8'h65: midi_note_to_tone_freq = 16'hb717; 112 | 8'h66: midi_note_to_tone_freq = 16'hc1fa; 113 | 8'h67: midi_note_to_tone_freq = 16'hcd83; 114 | 8'h68: midi_note_to_tone_freq = 16'hd9bc; 115 | 8'h69: midi_note_to_tone_freq = 16'he6ae; 116 | 8'h6a: midi_note_to_tone_freq = 16'hf466; 117 | default: midi_note_to_tone_freq = 16'hf466; // 16-bit increment counter doesn't have resolution for further notes 118 | // 8'h6b: midi_note_to_tone_freq = 16'h102ee; 119 | // 8'h6c: midi_note_to_tone_freq = 16'h11254; 120 | // 8'h6d: midi_note_to_tone_freq = 16'h122a3; 121 | // 8'h6e: midi_note_to_tone_freq = 16'h133ec; 122 | // 8'h6f: midi_note_to_tone_freq = 16'h1463b; 123 | // 8'h70: midi_note_to_tone_freq = 16'h159a1; 124 | // 8'h71: midi_note_to_tone_freq = 16'h16e2f; 125 | // 8'h72: midi_note_to_tone_freq = 16'h183f5; 126 | // 8'h73: midi_note_to_tone_freq = 16'h19b07; 127 | // 8'h74: midi_note_to_tone_freq = 16'h1b378; 128 | // 8'h75: midi_note_to_tone_freq = 16'h1cd5c; 129 | // 8'h76: midi_note_to_tone_freq = 16'h1e8cc; 130 | // 8'h77: midi_note_to_tone_freq = 16'h205dc; 131 | // 8'h78: midi_note_to_tone_freq = 16'h224a8; 132 | // 8'h79: midi_note_to_tone_freq = 16'h24547; 133 | // 8'h7a: midi_note_to_tone_freq = 16'h267d8; 134 | // 8'h7b: midi_note_to_tone_freq = 16'h28c77; 135 | // 8'h7c: midi_note_to_tone_freq = 16'h2b343; 136 | // 8'h7d: midi_note_to_tone_freq = 16'h2dc5e; 137 | // 8'h7e: midi_note_to_tone_freq = 16'h307ea; 138 | // 8'h7f: midi_note_to_tone_freq = 16'h3360e; 139 | endcase 140 | 141 | endfunction 142 | 143 | `endif 144 | -------------------------------------------------------------------------------- /examples/midi/midi_player.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MIDI_PLAYER__ 2 | `define __TINY_SYNTH_MIDI_PLAYER__ 3 | 4 | /* Using some slimmed-down versions of the voice code 5 | so that we can fit more voices in the budget */ 6 | `include "tone_generator_fixed_param.vh" 7 | `include "envelope_generator_fixed_param.vh" 8 | `include "voice_fixed_param.vh" 9 | 10 | /* Clifford Wolf's simpleuart */ 11 | `include "simpleuart.vh" 12 | 13 | /* .. and a wrapper for it that handles framing incoming messages into MIDI commands */ 14 | `include "midi_uart.vh" 15 | 16 | module midi_player #( 17 | parameter SAMPLE_BITS = 12 18 | ) ( 19 | input wire clk, 20 | input wire serial_rx, 21 | output wire serial_tx, 22 | output signed [SAMPLE_BITS-1:0] audio_data 23 | ); 24 | 25 | /* incoming midi data */ 26 | reg [7:0] midi_uart_data; 27 | reg midi_byte_clk; 28 | reg midi_event_valid; 29 | wire [7:0] midi_command; 30 | wire [6:0] midi_parameter_1; 31 | wire [6:0] midi_parameter_2; 32 | wire midi_event_ack; 33 | 34 | midi_uart midi_uart( 35 | .clk(clk), 36 | .serial_rx(serial_rx), .serial_tx(serial_tx), 37 | .midi_event_valid(midi_event_valid), 38 | .midi_command(midi_command), 39 | .midi_parameter_1(midi_parameter_1), 40 | .midi_parameter_2(midi_parameter_2), 41 | .midi_event_ack(midi_event_ack) 42 | ); 43 | 44 | /* CLOCK GENERATION; generate 1MHz clock for voice oscillators, and 44100Hz clock for sample output */ 45 | wire ONE_MHZ_CLK; 46 | clock_divider #(.DIVISOR(16)) mhz_clk_divider(.cin(clk), .cout(ONE_MHZ_CLK)); 47 | 48 | localparam SAMPLE_CLK_FREQ = 44100; 49 | 50 | // divide main clock down to 44100Hz for sample output (note this clock will have 51 | // a bit of jitter because 44.1kHz doesn't go evenly into 16MHz). 52 | wire SAMPLE_CLK; 53 | clock_divider #( 54 | .DIVISOR((16000000/SAMPLE_CLK_FREQ)) 55 | ) sample_clk_divider(.cin(clk), .cout(SAMPLE_CLK)); 56 | 57 | // number of voices to use 58 | // if you modify this, you'll also need to manually update the 59 | // mixing function below 60 | localparam NUM_VOICES = 8; 61 | 62 | // individual voices are mixed into here.. 63 | // output is wider than a single voice, and gets "clamped" (or "saturated") into clamped_voice_out. 64 | reg signed [SAMPLE_BITS+$clog2(NUM_VOICES)-1:0] raw_combined_voice_out; 65 | wire signed [SAMPLE_BITS-1:0] clamped_voice_out; 66 | 67 | localparam signed MAX_SAMPLE_VALUE = (2**(SAMPLE_BITS-1))-1; 68 | localparam signed MIN_SAMPLE_VALUE = -(2**(SAMPLE_BITS-1)); 69 | 70 | assign clamped_voice_out = (raw_combined_voice_out > MAX_SAMPLE_VALUE) 71 | ? MAX_SAMPLE_VALUE 72 | : ((raw_combined_voice_out < MIN_SAMPLE_VALUE) 73 | ? MIN_SAMPLE_VALUE 74 | : raw_combined_voice_out[SAMPLE_BITS-1:0]); 75 | 76 | 77 | reg [NUM_VOICES-1:0] voice_gate; /* MIDI gates */ 78 | reg [NUM_VOICES-1:0] voice_idle; /* is this voice idle / ready to accept a new note? */ 79 | reg [15:0] voice_frequency[0:NUM_VOICES-1]; /* frequency of the voice */ 80 | reg [6:0] voice_note[0:NUM_VOICES-1]; /* midi note that is playing on this voice */ 81 | wire signed[SAMPLE_BITS-1:0] voice_samples[0:NUM_VOICES-1]; // samples for each voice 82 | 83 | // MIXER: this adds the output from the 8 voices together 84 | always @(posedge SAMPLE_CLK) begin 85 | raw_combined_voice_out <= (voice_samples[0]+voice_samples[1]+voice_samples[2]+voice_samples[3] 86 | +voice_samples[4]+voice_samples[5]+voice_samples[6]+voice_samples[7])>>>2; 87 | end 88 | 89 | 90 | // pass voice output through a low-pass filter, and a flanger to spice it up a little 91 | wire signed[SAMPLE_BITS-1:0] filter_out; 92 | 93 | filter_ewma #(.DATA_BITS(SAMPLE_BITS)) filter(.clk(SAMPLE_CLK), .s_alpha(25), .din(clamped_voice_out), .dout(filter_out)); 94 | flanger #(.SAMPLE_BITS(SAMPLE_BITS)) flanger(.sample_clk(SAMPLE_CLK), .din(filter_out), .dout(audio_data)); 95 | 96 | localparam WAVE_NOISE=4; 97 | localparam WAVE_PULSE=3; 98 | localparam WAVE_SAW=2; 99 | localparam WAVE_TRIANGLE=1; 100 | 101 | localparam WAVEFORM=WAVE_SAW; 102 | localparam [7:0] SUSTAIN_VOLUME=128; 103 | 104 | localparam ACCUMULATOR_BITS = 26; 105 | localparam ACCUMULATOR_SIZE = 2**ACCUMULATOR_BITS; 106 | localparam ACCUMULATOR_MAX = ACCUMULATOR_SIZE-1; 107 | 108 | 109 | `define CALCULATE_PHASE_INCREMENT(n) $rtoi(ACCUMULATOR_SIZE / ($itor(n) * SAMPLE_CLK_FREQ)) 110 | 111 | localparam ATTACK_INC = `CALCULATE_PHASE_INCREMENT(0.038); 112 | localparam DECAY_INC = `CALCULATE_PHASE_INCREMENT(0.038); 113 | localparam RELEASE_INC = `CALCULATE_PHASE_INCREMENT(0.8); 114 | 115 | 116 | generate 117 | genvar i; 118 | /* generate some voices and wire them to the per-MIDI-note gates */ 119 | for (i=0; i | MIDI framer | ---------------------> 11 | * |_____________| 12 | * 13 | * MIDI commands have quite a simple structure. 14 | * 15 | * The first (command) byte can be recognised by the fact that has the MSB set. 16 | * 17 | * Valid commands for our purposes range from 0x80 to 0xEF. 18 | * 19 | * Command Meaning # parameters param 1 param 2 20 | * 0x80 Note-off 2 key velocity 21 | * 0x90 Note-on 2 key velocity 22 | * 0xA0 Aftertouch 2 key touch 23 | * 0xB0 Continuous controller 2 controller # controller value 24 | * 0xC0 Patch change 1 instrument # 25 | * 0xD0 Channel Pressure 1 pressure 26 | * 0xE0 Pitch bend 2 lsb (7 bits) msb (7 bits) 27 | * 0xF0 (non-musical commands) 28 | * 29 | * NOTE: MIDI allows a single "command" byte to be followed by multiple 30 | * sets of parameters - the command byte is remembered. 31 | */ 32 | 33 | module midi_uart( 34 | input [7:0] din, /* input raw byte data from MIDI bus */ 35 | input din_clk, /* input clock (pos edge) */ 36 | input clk, 37 | input serial_rx, 38 | output serial_tx, 39 | 40 | input midi_event_ack, 41 | output [7:0] midi_command, /* output: MIDI command byte */ 42 | output [6:0] midi_parameter_1, /* output: MIDI parameter #1 */ 43 | output [6:0] midi_parameter_2, /* output: MIDI parameter #2 */ 44 | output reg midi_event_valid /* output: MIDI command and parameters contain valid data */ 45 | ); 46 | 47 | reg [7:0] uart_rx_data; 48 | reg uart_read_ack; 49 | reg uart_recv_data_valid; 50 | 51 | reg uart_tx_busy; /* not used */ 52 | 53 | 54 | reg [3:0] state; /* number of MIDI bytes received (including command) */ 55 | reg [21:0] in_flight_midi_message; 56 | reg [2:0] in_flight_expected_param_count; 57 | 58 | simpleuart #(.CLOCK_FREQUENCY(16000000), .BAUD_RATE(31250)) uart( 59 | .clk(clk), .resetn(1'b1), 60 | .ser_rx(serial_rx), 61 | .reg_dat_re(uart_read_ack), 62 | .reg_dat_do(uart_rx_data), 63 | .recv_buf_valid(uart_recv_data_valid), 64 | 65 | /* we never write to the UART */ 66 | .ser_tx(serial_tx), 67 | .reg_dat_we(1'b0), 68 | .reg_dat_di(8'd0), 69 | .tx_busy(uart_tx_busy) 70 | ); 71 | 72 | initial begin 73 | midi_event_valid <= 1'b0; 74 | midi_command <= 8'h00; 75 | midi_parameter_1 <= 7'h00; 76 | midi_parameter_2 <= 7'h00; 77 | 78 | in_flight_expected_param_count <= 2'h0; 79 | end 80 | 81 | function [2:0] expected_midi_parameter_count( 82 | input [3:0] command 83 | ); 84 | begin 85 | case (command) 86 | 4'hc: expected_midi_parameter_count = 3'd1; /* patch change */ 87 | 4'hd: expected_midi_parameter_count = 3'd1; /* channel pressure */ 88 | default: expected_midi_parameter_count = 3'd2; 89 | endcase 90 | end 91 | endfunction 92 | 93 | always @(posedge clk) 94 | begin 95 | if (midi_event_ack) begin 96 | midi_event_valid <= 0; 97 | end 98 | 99 | if (uart_recv_data_valid && !uart_read_ack) begin 100 | // we've just read a new byte from the UART 101 | if (uart_rx_data[7:4] == 4'hf) begin 102 | state <= 4'hf; 103 | end else if (uart_rx_data[7] === 1'b1) begin 104 | // the MSB was set, so this signifies a new MIDI "command" 105 | state <= 1; 106 | in_flight_expected_param_count <= expected_midi_parameter_count(uart_rx_data[7:4]); 107 | in_flight_midi_message <= { uart_rx_data, 14'b0 }; 108 | end else begin 109 | // MSB was not set, so this is a parameter byte 110 | case (state) 111 | 1: begin // waiting for parameter #1 to arrive 112 | if (in_flight_expected_param_count == 1) 113 | begin 114 | // if we only wanted one parameter, then we've got it, so 115 | // clock this out as a valid MIDI event 116 | midi_command <= in_flight_midi_message[21:14]; 117 | midi_parameter_1 <= uart_rx_data; 118 | midi_parameter_2 <= 7'b0; 119 | midi_event_valid <= 1'b1; 120 | state <= 1; 121 | end else begin 122 | // our MIDI command expected more than one parameter, 123 | // so we need to wait for the next to arrive. 124 | in_flight_midi_message <= { in_flight_midi_message[21:14], uart_rx_data[6:0], 7'b0 }; 125 | state <= state + 1; 126 | end 127 | end 128 | 2: begin // waiting for parameter #2 to arrive 129 | if (in_flight_expected_param_count == 2) 130 | begin 131 | // we were expecting two parameters, we've got them, 132 | // so clock them out as a valid MIDI event 133 | midi_command <= in_flight_midi_message[21:14]; 134 | midi_parameter_1 <= in_flight_midi_message[13:7]; 135 | midi_parameter_2 <= uart_rx_data[6:0]; 136 | midi_event_valid <= 1'b1; 137 | end 138 | // we don't support commands with more than 2 parameters, 139 | // so now we transition to a "null" state, until the next 140 | // command arrives. 141 | state <= 1; 142 | end 143 | default: begin 144 | state <= 4'hf; 145 | end 146 | endcase 147 | end 148 | 149 | uart_read_ack <= 1'b1; 150 | end else begin 151 | uart_read_ack <= 1'b0; 152 | end 153 | 154 | end 155 | 156 | endmodule 157 | 158 | `endif 159 | -------------------------------------------------------------------------------- /examples/midi/pins.pcf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # TinyFPGA BX constraint file (.pcf) 4 | # 5 | ############################################################################### 6 | # 7 | # Copyright (c) 2018, Luke Valenty 8 | # All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | # 13 | # 1. Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | # 2. Redistributions in binary form must reproduce the above copyright notice, 16 | # this list of conditions and the following disclaimer in the documentation 17 | # and/or other materials provided with the distribution. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | # 30 | # The views and conclusions contained in the software and documentation are those 31 | # of the authors and should not be interpreted as representing official policies, 32 | # either expressed or implied, of the project. 33 | # 34 | ############################################################################### 35 | 36 | #### 37 | # TinyFPGA BX information: https://github.com/tinyfpga/TinyFPGA-BX/ 38 | #### 39 | 40 | # Left side of board 41 | set_io --warn-no-port PIN_1 A2 42 | #set_io --warn-no-port PIN_2 A1 43 | #set_io --warn-no-port PIN_3 B1 44 | #set_io --warn-no-port PIN_4 C2 45 | #set_io --warn-no-port PIN_5 C1 46 | #set_io --warn-no-port PIN_6 D2 47 | #set_io --warn-no-port PIN_7 D1 48 | #set_io --warn-no-port PIN_8 E2 49 | #set_io --warn-no-port PIN_9 E1 50 | #set_io --warn-no-port PIN_10 G2 51 | #set_io --warn-no-port PIN_11 H1 52 | #set_io --warn-no-port PIN_12 J1 53 | #set_io --warn-no-port PIN_13 H2 54 | 55 | # Right side of board 56 | set_io --warn-no-port PIN_14 H9 57 | set_io --warn-no-port PIN_15 D9 58 | #set_io --warn-no-port PIN_16 D8 59 | #set_io --warn-no-port PIN_17 C9 60 | #set_io --warn-no-port PIN_18 A9 61 | #set_io --warn-no-port PIN_19 B8 62 | #set_io --warn-no-port PIN_20 A8 63 | #set_io --warn-no-port PIN_21 B7 64 | #set_io --warn-no-port PIN_22 A7 65 | #set_io --warn-no-port PIN_23 B6 66 | #set_io --warn-no-port PIN_24 A6 67 | 68 | # SPI flash interface on bottom of board 69 | #set_io --warn-no-port SPI_SS F7 70 | #set_io --warn-no-port SPI_SCK G7 71 | #set_io --warn-no-port SPI_IO0 G6 72 | #set_io --warn-no-port SPI_IO1 H7 73 | #set_io --warn-no-port SPI_IO2 H4 74 | #set_io --warn-no-port SPI_IO3 J8 75 | 76 | # General purpose pins on bottom of board 77 | #set_io --warn-no-port PIN_25 G1 78 | #set_io --warn-no-port PIN_26 J3 79 | #set_io --warn-no-port PIN_27 J4 80 | #set_io --warn-no-port PIN_28 G9 81 | #set_io --warn-no-port PIN_29 J9 82 | #set_io --warn-no-port PIN_30 E8 83 | #set_io --warn-no-port PIN_31 J2 84 | 85 | # LED 86 | #set_io --warn-no-port LED B3 87 | 88 | # USB 89 | #set_io --warn-no-port USBP B4 90 | #set_io --warn-no-port USBN A4 91 | set_io --warn-no-port USBPU A3 92 | 93 | # 16MHz clock 94 | set_io --warn-no-port CLK B2 # input 95 | -------------------------------------------------------------------------------- /examples/midi/simpleuart.vh: -------------------------------------------------------------------------------- 1 | /* 2 | * PicoSoC - A simple example SoC using PicoRV32 3 | * 4 | * Copyright (C) 2017 Clifford Wolf 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | */ 19 | 20 | /* 21 | * Some minor modifications have been made to Clifford's code to remove 22 | * some of the SoC integration complexities - I've also commented things 23 | * for my own understanding. 24 | * 25 | */ 26 | `ifndef __PICOSOC_SIMPLEUART__ 27 | `define __PICOSOC_SIMPLEUART__ 28 | 29 | module simpleuart #( 30 | parameter CLOCK_FREQUENCY = 16000000, 31 | parameter BAUD_RATE = 115200 32 | ) ( 33 | input clk, 34 | input resetn, /* active low reset */ 35 | 36 | output ser_tx, /* serial transmit out */ 37 | input ser_rx, /* serial receive in */ 38 | 39 | output recv_buf_valid, 40 | input reg_dat_re, 41 | output [7:0] reg_dat_do, 42 | 43 | input reg_dat_we, 44 | input [7:0] reg_dat_di, 45 | output tx_busy); 46 | 47 | localparam cfg_divider = $rtoi(CLOCK_FREQUENCY / BAUD_RATE); 48 | 49 | /* 50 | * RECEIVE related registers 51 | */ 52 | 53 | /* =============================================================== 54 | * TABLE OF RECEIVE STATES 55 | * =============================================================== 56 | * 57 | * STATE | DESCRIPTION 58 | * ------+------------------------- 59 | * 0 | waiting for start bit 60 | * 1 | start bit detected, waiting for middle of start bit 61 | * 2 | waiting for middle of bit 0 62 | * 3 | waiting for middle of bit 1 63 | * 4 | waiting for middle of bit 2 64 | * 5 | waiting for middle of bit 3 65 | * 6 | waiting for middle of bit 4 66 | * 7 | waiting for middle of bit 5 67 | * 8 | waiting for middle of bit 6 68 | * 9 | waiting for middle of bit 7 69 | * 10 | waiting for middle of stop bit 70 | */ 71 | reg [3:0] recv_state; /* recv_state is essentially a bit counter - indicating which bit the receiver is expecting */ 72 | 73 | reg [31:0] recv_divcnt; /* up-counter, to count cycles for current bit */ 74 | reg [7:0] recv_pattern; /* currently received pattern; clocked in from MSB end */ 75 | reg [7:0] recv_buf_data; /* previously received byte */ 76 | reg recv_buf_valid; /* flag to indicate that the buffered receive byte is valid, and can be clocked out with reg_dat_re */ 77 | 78 | 79 | /* data out line is either data from the buffer (if the data is available and valid), 80 | * or it's 0xff otherwise. 81 | */ 82 | assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0; 83 | assign tx_busy = reg_dat_we && (send_bitcnt || send_dummy); 84 | 85 | /* ===================================================================================================================== */ 86 | 87 | /* 88 | * TRANSMIT related registers 89 | */ 90 | reg [9:0] send_pattern; /* current pattern being clocked out on the serial line */ 91 | reg [3:0] send_bitcnt; /* number of bits remaining to be sent */ 92 | reg [31:0] send_divcnt; /* number of clock cycles counted for the current bit */ 93 | reg send_dummy; /* flag to indicate that the next "slot" is empty */ 94 | 95 | /* reg_dat_wait is asserted when an attempted write is not possible because 96 | * the serial line is busy because it's either currently sending data 97 | * (send_bitcnt is non-zero) or is about to send a dummy slot (send_dummy is 1). 98 | */ 99 | assign tx_busy = reg_dat_we && (send_bitcnt || send_dummy); 100 | 101 | always @(posedge clk) begin 102 | if (!resetn) begin 103 | recv_state <= 0; 104 | recv_divcnt <= 0; 105 | recv_pattern <= 0; 106 | recv_buf_data <= 0; 107 | recv_buf_valid <= 0; 108 | end else begin 109 | recv_divcnt <= recv_divcnt + 1; 110 | 111 | /* if data has been clocked out, reset buf valid flag to 0 */ 112 | if (reg_dat_re) 113 | recv_buf_valid <= 0; 114 | 115 | case (recv_state) 116 | 0: begin 117 | /* if our serial input has been brought low, we're now 118 | * expecting the start bit */ 119 | if (!ser_rx) 120 | recv_state <= 1; 121 | 122 | recv_divcnt <= 0; 123 | end 124 | 1: begin 125 | /* state 1 means we're waiting for the middle of the start bit. */ 126 | if (ser_rx) begin 127 | // we had a false start bit; reset and try again 128 | recv_state <= 0; 129 | end else begin 130 | if (2*recv_divcnt > cfg_divider) begin 131 | /* at this point we've aligned ourselves half-way 132 | * through the start-bit (notice the 2x multiplier above), 133 | * so we reset the counter to zero, and wait for the next 134 | * full-bit cycle(s) so we can sample the middle of the incoming bits. 135 | */ 136 | recv_state <= 2; 137 | recv_divcnt <= 0; 138 | end 139 | end 140 | end 141 | 10: begin 142 | if (recv_divcnt > cfg_divider) begin 143 | /* at this point we're in the middle of receiving the 144 | * stop bit, and rather than doing anything with it, we 145 | * use this as an opportunity to clock out the newly 146 | * received byte by setting the recv_buf_valid flag, 147 | * and then bounce back to state 0. (note: stop-bit is 148 | * logic-level high, so we're ready to look for the next 149 | * high-low transition to signal the next bit). 150 | */ 151 | recv_buf_data <= recv_pattern; 152 | recv_buf_valid <= 1; 153 | recv_state <= 0; 154 | end 155 | end 156 | default: begin 157 | /* states 2-9; clocking in bits 0-7. */ 158 | if (recv_divcnt > cfg_divider) begin 159 | recv_pattern <= {ser_rx, recv_pattern[7:1]}; 160 | recv_state <= recv_state + 1; 161 | recv_divcnt <= 0; 162 | end 163 | end 164 | endcase 165 | end 166 | end 167 | 168 | /* ==================================================================== 169 | * TRANSMIT LOGIC 170 | * ==================================================================== 171 | */ 172 | 173 | // clock out bit zero of send_pattern 174 | assign ser_tx = send_pattern[0]; 175 | 176 | always @(posedge clk) begin 177 | send_divcnt <= send_divcnt + 1; 178 | if (!resetn) begin 179 | /* reset line low, so reset all signals */ 180 | send_pattern <= ~0; 181 | send_bitcnt <= 0; 182 | send_divcnt <= 0; 183 | send_dummy <= 1; 184 | end else begin 185 | if (send_dummy && !send_bitcnt) begin 186 | /* send_dummy requested for next timing slot, 187 | * reset bit count to 15, set send pattern to all 1's, 188 | * clear send_dummy flag */ 189 | send_pattern <= ~0; 190 | send_bitcnt <= 15; 191 | send_divcnt <= 0; 192 | send_dummy <= 0; 193 | end else 194 | if (reg_dat_we && !send_bitcnt) begin 195 | /* write-enable set, and we're not in the process of sending 196 | * any bits, so pull the send_pattern from our data-input port, 197 | * (surrounded by start and stop bits) 198 | */ 199 | send_pattern <= {1'b1, reg_dat_di, 1'b0}; 200 | send_bitcnt <= 10; 201 | send_divcnt <= 0; 202 | end else 203 | if (send_divcnt > cfg_divider && send_bitcnt) begin 204 | /* 205 | * we're ready for the next bit, so rotate send_pattern one 206 | * bit to the right (and fill MSB with a 1). Also 207 | * decrement the number of bits left to send, and 208 | * reset the counter. 209 | */ 210 | send_pattern <= {1'b1, send_pattern[9:1]}; 211 | send_bitcnt <= send_bitcnt - 1; 212 | send_divcnt <= 0; 213 | end 214 | end 215 | end 216 | endmodule 217 | 218 | `endif 219 | -------------------------------------------------------------------------------- /examples/midi/tone_generator_fixed_param.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TONE_GENERATOR_AGGREGATE_FIXED_PARAM__ 2 | `define __TINY_SYNTH_TONE_GENERATOR_AGGREGATE_FIXED_PARAM__ 3 | 4 | `include "../../hdl/tone_generator_saw.vh" 5 | `include "../../hdl/tone_generator_pulse.vh" 6 | `include "../../hdl/tone_generator_triangle.vh" 7 | `include "../../hdl/tone_generator_noise.vh" 8 | 9 | /* ========================================================= 10 | * Phase-accumulator tone-generator (with fixed parameters) 11 | * ========================================================= 12 | * 13 | * This is a version of tone_generator with fixed (immutable) 14 | * parameters - this is so it can synthesise down to a simpler 15 | * implementation. 16 | * 17 | * This module aggregates the other tone generators together. 18 | * 19 | * It houses the accumulator and the logic for incrementing it 20 | * at a given frequency. 21 | * 22 | * It allows individual tone generators to be selected and logically 23 | * "ANDed" into the output stream. 24 | * 25 | * It also has provision for syncing oscillators together based on 26 | * when they overflow, and proving the accumulator MSB for ring 27 | * modulation purposes. 28 | */ 29 | module tone_generator_fixed_param #( 30 | parameter FREQ_BITS = 16, 31 | parameter PULSEWIDTH_BITS = 12, 32 | parameter OUTPUT_BITS = 12, 33 | parameter ACCUMULATOR_BITS = 24, 34 | parameter WAVEFORM = 1 35 | ) ( 36 | input [FREQ_BITS-1:0] tone_freq, 37 | input [PULSEWIDTH_BITS-1:0] pulse_width, 38 | input main_clk, 39 | input sample_clk, 40 | input rst, 41 | output reg signed [OUTPUT_BITS-1:0] dout, 42 | output wire accumulator_msb, 43 | output wire accumulator_overflow, 44 | 45 | input wire en_ringmod, 46 | input wire ringmod_source, 47 | 48 | input wire en_sync, 49 | input wire sync_source 50 | ); 51 | 52 | reg [ACCUMULATOR_BITS:0] accumulator; 53 | 54 | wire [OUTPUT_BITS-1:0] wave_out; 55 | 56 | generate 57 | if (WAVEFORM == 1) 58 | tone_generator_triangle #( 59 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 60 | .OUTPUT_BITS(OUTPUT_BITS) 61 | ) triangle_generator ( 62 | .accumulator(accumulator[ACCUMULATOR_BITS-1:0]), 63 | .dout(wave_out), 64 | .en_ringmod(en_ringmod), 65 | .ringmod_source(ringmod_source) 66 | ); 67 | if (WAVEFORM == 2) 68 | tone_generator_saw #( 69 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 70 | .OUTPUT_BITS(OUTPUT_BITS) 71 | ) saw( 72 | .accumulator(accumulator[ACCUMULATOR_BITS-1:0]), 73 | .dout(wave_out) 74 | ); 75 | if (WAVEFORM == 3) 76 | tone_generator_pulse #( 77 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 78 | .OUTPUT_BITS(OUTPUT_BITS), 79 | .PULSEWIDTH_BITS(PULSEWIDTH_BITS) 80 | ) pulse( 81 | .accumulator(accumulator[ACCUMULATOR_BITS-1:0]), 82 | .dout(wave_out), 83 | .pulse_width(pulse_width) 84 | ); 85 | if (WAVEFORM == 4) 86 | tone_generator_noise #( 87 | .OUTPUT_BITS(OUTPUT_BITS) 88 | ) noise(.clk(accumulator[18]), .rst(rst), .dout(wave_out)); 89 | endgenerate 90 | 91 | always @(posedge main_clk) begin 92 | if (en_sync && sync_source) 93 | begin 94 | accumulator <= 0; 95 | end 96 | else 97 | begin 98 | accumulator <= accumulator[ACCUMULATOR_BITS-1:0] + tone_freq; 99 | end 100 | end 101 | 102 | assign accumulator_overflow = (accumulator[ACCUMULATOR_BITS]); /* used for syncing to other oscillators */ 103 | assign accumulator_msb = accumulator[ACCUMULATOR_BITS-1]; 104 | 105 | always @(posedge sample_clk) begin 106 | dout <= wave_out ^ (2**(OUTPUT_BITS-1)); 107 | end 108 | 109 | endmodule 110 | 111 | `endif 112 | -------------------------------------------------------------------------------- /examples/midi/top.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny-synth example: playing notes based on MIDI input 3 | */ 4 | 5 | `define __TINY_SYNTH_ROOT_FOLDER "../.." 6 | `include "../../hdl/tiny-synth-all.vh" 7 | `include "midi_player.vh" 8 | 9 | // look in pins.pcf for all the pin names on the TinyFPGA BX board 10 | module top ( 11 | `ifdef blackice 12 | input CLK_100, // 100MHz clock 13 | `else 14 | input CLK, // 16MHz clock 15 | `endif 16 | output USBPU, // USB pull-up resistor 17 | input PIN_14, // serial (MIDI) data in 18 | output PIN_15, // serial (MIDI) data out 19 | output PIN_1); /* audio out */ 20 | 21 | `ifdef blackice 22 | wire CLK; 23 | pll pll0 (.clock_in(CLK_100), .clock_out(CLK)); 24 | `endif 25 | 26 | // drive USB pull-up resistor to '0' to disable USB 27 | assign USBPU = 0; 28 | 29 | wire serial_rx; 30 | SB_IO #( 31 | .PIN_TYPE(6'b0000_01), 32 | .PULLUP(1'b0) 33 | ) serial_rx_pin_conf ( 34 | .PACKAGE_PIN(PIN_14), 35 | .D_IN_0(serial_rx) 36 | ); 37 | 38 | localparam SAMPLE_BITS = 12; 39 | 40 | wire signed [SAMPLE_BITS-1:0] final_mix; 41 | midi_player #(.SAMPLE_BITS(SAMPLE_BITS)) midi_player(.clk(CLK), .serial_rx(serial_rx), .serial_tx(PIN_15), .audio_data(final_mix)); 42 | 43 | pdm_dac #(.DATA_BITS(SAMPLE_BITS)) dac1( 44 | .din(final_mix), 45 | .clk(CLK), // DAC runs at full 16MHz speed. 46 | .dout(PIN_1) 47 | ); 48 | 49 | endmodule 50 | -------------------------------------------------------------------------------- /examples/midi/voice_fixed_param.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_VOICE_FIXED_PARAM__ 2 | `define __TINY_SYNTH_VOICE_FIXED_PARAM__ 3 | 4 | `include "tone_generator_fixed_param.vh" 5 | `include "envelope_generator_fixed_param.vh" 6 | `include "../../hdl/amplitude_modulator.vh" 7 | 8 | module voice_fixed_param #( 9 | parameter OUTPUT_BITS = 12, 10 | parameter FREQ_BITS = 16, 11 | parameter PULSEWIDTH_BITS = 12, 12 | parameter ACCUMULATOR_BITS = 24, 13 | parameter SAMPLE_CLK_FREQ = 44100, 14 | parameter ATTACK_INC = 1000, 15 | parameter DECAY_INC = 1000, 16 | parameter [7:0] SUSTAIN_VOLUME = 128, 17 | parameter RELEASE_INC = 1000, 18 | parameter WAVEFORM = 1 19 | )( 20 | input [FREQ_BITS-1:0] tone_freq, 21 | input [PULSEWIDTH_BITS-1:0] pulse_width, 22 | input main_clk, 23 | input sample_clk, 24 | input rst, 25 | output wire signed [OUTPUT_BITS-1:0] dout, 26 | output wire accumulator_msb, /* used to feed ringmod on another voice */ 27 | output wire accumulator_overflow, /* set when accumulator = 0; used to sync with another oscillator */ 28 | output wire is_idle, 29 | input en_ringmod, 30 | input wire ringmod_source, 31 | input en_sync, 32 | input wire sync_source, 33 | 34 | // envelope generator params 35 | input wire gate 36 | ); 37 | 38 | wire signed [OUTPUT_BITS-1:0] tone_generator_data; 39 | wire[7:0] envelope_amplitude; 40 | 41 | tone_generator_fixed_param #( 42 | .FREQ_BITS(FREQ_BITS), 43 | .PULSEWIDTH_BITS(PULSEWIDTH_BITS), 44 | .OUTPUT_BITS(OUTPUT_BITS), 45 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 46 | .WAVEFORM(WAVEFORM) 47 | ) tone_generator ( 48 | .tone_freq(tone_freq), 49 | .pulse_width(pulse_width), 50 | .main_clk(main_clk), 51 | .sample_clk(sample_clk), 52 | .rst(rst), 53 | .dout(tone_generator_data), 54 | .accumulator_msb(accumulator_msb), 55 | .accumulator_overflow(accumulator_overflow), 56 | .en_sync(en_sync), 57 | .sync_source(sync_source), 58 | .en_ringmod(en_ringmod), 59 | .ringmod_source(ringmod_source) 60 | ); 61 | 62 | envelope_generator_fixed_param #( 63 | .SAMPLE_CLK_FREQ(SAMPLE_CLK_FREQ), 64 | .ATTACK_INC(ATTACK_INC), 65 | .DECAY_INC(DECAY_INC), 66 | .SUSTAIN_VOLUME(SUSTAIN_VOLUME), 67 | .RELEASE_INC(RELEASE_INC) 68 | ) envelope( 69 | .clk(sample_clk), 70 | .rst(rst), 71 | .gate(gate), 72 | .is_idle(is_idle), 73 | .amplitude(envelope_amplitude) 74 | ); 75 | 76 | amplitude_modulator #(.DATA_BITS(OUTPUT_BITS)) modulator( 77 | .clk(sample_clk), 78 | .din(tone_generator_data), 79 | .amplitude(envelope_amplitude), 80 | .dout(dout) 81 | ); 82 | 83 | endmodule 84 | 85 | `endif 86 | -------------------------------------------------------------------------------- /examples/midi2/Makefile: -------------------------------------------------------------------------------- 1 | VERILOG_FILES=top.v ../../blackice//pll.v 2 | PCF_FILE = ../../blackice/blackice.pcf 3 | 4 | include ../../blackice/blackice.mk 5 | -------------------------------------------------------------------------------- /examples/midi2/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A simple MIDI-based example for tiny-synth. 4 | 5 | This example is an eight-voice MIDI-connected synthesizer. 6 | 7 | ## Principle of operation 8 | 9 | The MIDI interface uses Clifford Wolf's simpleuart code (slightly modified so 10 | that it could be used outside of the PicoSoC environment) to read data from the 11 | MIDI interface. 12 | 13 | `simpleuart` is wrapped with the `midi_uart` module, which essentially waits for 14 | whole MIDI frames (command+parameter messages) to arrive before passing them 15 | up to the next layer. 16 | 17 | `midi_player` instantiates 8 voices, and keeps track which voices are available, 18 | and which are busy playing. When a "note-on" MIDI message arrives, the first 19 | available voice is chosen, and the voice is assigned / gated appropriately. 20 | "note-off" messages simply find all voices playing the correct note, and 21 | gate them off. 22 | 23 | # Before you start 24 | 25 | This example requires a bit of extra circuitry - namely MIDI input requires a H11L1 26 | opto-coupler to isolate your instrument, and to help prevent ground loops as per 27 | the MIDI specification. 28 | 29 | ## Audio output 30 | 31 | As with the other tiny-synth examples, audio-out requires some analog circuitry: 32 | 33 | 34 | The below circuit can be used to filter the output from `PIN_1`. 35 | 36 | ``` 37 | e.g. 330 ohm eg. 10uF 38 | PIN_1 >---./\/\/\.---o------| |-------> Analog out >-- 39 | | 40 | --- 41 | --- eg. 0.1uF 42 | | 43 | | 44 | --- GND 45 | - 46 | ``` 47 | 48 | ## MIDI input 49 | 50 | MIDI input is via a 5-pin DIN MIDI connector (I used a breadboard-friendly one), 51 | and an H11L1 opto-coupler, with a few passives. 52 | 53 | The circuit I used is described below. 54 | 55 | ### MIDI connector pinout 56 | 57 | The pins in the MIDI connector are numbered as below. Imagine this is a female 58 | DIN socket, and you're looking at it head-on. 59 | 60 | ``` 61 | 62 | 5-PIN MIDI female socket; looking head-on 63 | 64 | - '|_|' - 65 | ' ' 66 | | 1 3 | 67 | . 4 5 . 68 | . 2 . 69 | `-----' 70 | 71 | ``` 72 | 73 | ### Circuit diagram 74 | 75 | ``` 76 | ___ GND 77 | | 78 | H11L1 --- 1uF 79 | _____________________ --- 80 | 220ohm 1 | | 6 | 81 | MIDI_IN PIN4 -------./\/\.----o-----------|---. ----.o--|-------o----------o-----------> 3v3 82 | _|_ | _|_ | . | 4 \ 83 | 1N4148 /_\ | \ / '' | \=\ |o-|----------. / 270ohm 84 | | 2 | ~~~ | . | 5 | \ 85 | MIDI_IN PIN5 -----------------o-----------|---' ----'o--|----. `-------o-----------> UART RX (PIN 14 on TinyFPGA BX) 86 | |___________________| | 87 | --- GND 88 | ``` 89 | -------------------------------------------------------------------------------- /examples/midi2/apio.ini: -------------------------------------------------------------------------------- 1 | [env] 2 | board = TinyFPGA-BX 3 | 4 | -------------------------------------------------------------------------------- /examples/midi2/envelope_generator_fixed_param.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_ENV_GENERATOR_FIXED_PARAM__ 2 | `define __TINY_SYNTH_ENV_GENERATOR_FIXED_PARAM__ 3 | 4 | `include "../../hdl/eight_bit_exponential_decay_lookup.vh" 5 | 6 | /* =================== 7 | * Envelope generator 8 | * =================== 9 | * 10 | * Creates an 8-bit ADSR (attack, decay, sustain, release) volume envelope. 11 | * 12 | * .. 13 | * A . `. D S 14 | * . `---------- 15 | * . . R 16 | * . ` . 17 | * ----------------------------> 18 | * t 19 | * 20 | * By modulating the tone generator output with an ADSR envelope like this, 21 | * it's possible to create many interesting sounds. 22 | * 23 | * The input parameters are described in README.md. 24 | * 25 | * Principle of operation: 26 | * 27 | * The envelope generator is a state machine that makes use of an accumulator for 28 | * generation of the output wave shape & timing. For each of the A/D/R stages, 29 | * the state is advanced when the accumulator overflows. 30 | * 31 | * The envelope is 'triggered' by a gate signal, and as long as gate is held 32 | * high, the envelope won't transition past the sustain phase. When gate is 33 | * released, the envelope will transition into the release phase. 34 | * 35 | * The decay and release phases use an exponential fall-off. 36 | */ 37 | module envelope_generator_fixed_param #( 38 | parameter SAMPLE_CLK_FREQ = 44100, 39 | parameter ACCUMULATOR_BITS = 26, 40 | parameter ATTACK_INC = 1000, 41 | parameter DECAY_INC = 1000, 42 | parameter [7:0] SUSTAIN_VOLUME = 8'd128, 43 | parameter RELEASE_INC = 1000 44 | ) 45 | ( 46 | input clk, 47 | input gate, 48 | output is_idle, 49 | output reg [7:0] amplitude, 50 | input rst); 51 | 52 | localparam ACCUMULATOR_SIZE = 2**ACCUMULATOR_BITS; 53 | localparam ACCUMULATOR_MAX = ACCUMULATOR_SIZE-1; 54 | 55 | reg [ACCUMULATOR_BITS:0] accumulator; 56 | reg [16:0] accumulator_inc; /* value to add to accumulator */ 57 | 58 | // calculate the amount to add to the accumulator each clock cycle to 59 | // achieve a full-scale value in n number of seconds. (n can be fractional seconds) 60 | `define CALCULATE_PHASE_INCREMENT(n) $rtoi(ACCUMULATOR_SIZE / ($itor(n) * SAMPLE_CLK_FREQ)) 61 | 62 | // localparam ATTACK_INC = `CALCULATE_PHASE_INCREMENT(ATTACK_SECONDS); 63 | // localparam DECAY_INC = `CALCULATE_PHASE_INCREMENT(DECAY_SECONDS); 64 | // localparam RELEASE_INC = `CALCULATE_PHASE_INCREMENT(RELEASE_SECONDS); 65 | localparam [7:0] SUSTAIN_GAP = 255 - SUSTAIN_VOLUME; 66 | 67 | // Envelope states 68 | localparam OFF = 3'd0; 69 | localparam ATTACK = 3'd1; 70 | localparam DECAY = 3'd2; 71 | localparam SUSTAIN = 3'd3; 72 | localparam RELEASE = 3'd4; 73 | 74 | reg[2:0] state; 75 | 76 | assign is_idle = (state == OFF); 77 | 78 | initial begin 79 | state = OFF; 80 | amplitude = 0; 81 | accumulator = 0; 82 | end 83 | 84 | reg [16:0] dectmp; /* scratch-register for intermediate result of decay scaling */ 85 | reg [16:0] reltmp; /* scratch-register for intermediate-result of release-scaling */ 86 | 87 | wire [7:0] exp_out; // exponential decay mapping of accumulator output; used for decay and release cycles 88 | eight_bit_exponential_decay_lookup exp_lookup(.din(accumulator[ACCUMULATOR_BITS-1 -: 8]), .dout(exp_out)); 89 | 90 | /* calculate the next state of the envelope generator based on 91 | the state that we've just moved past, and the gate signal */ 92 | function [2:0] next_state; 93 | input [2:0] s; 94 | input g; 95 | begin 96 | case ({ s, g }) 97 | { ATTACK, 1'b0 }: next_state = RELEASE; /* attack, gate off => skip decay, sustain; go to release */ 98 | { ATTACK, 1'b1 }: next_state = DECAY; /* attack, gate still on => decay */ 99 | { DECAY, 1'b0 }: next_state = RELEASE; /* decay, gate off => skip sustain; go to release */ 100 | { DECAY, 1'b1 }: next_state = SUSTAIN; /* decay, gate still on => sustain */ 101 | { SUSTAIN, 1'b0 }: next_state = RELEASE; /* sustain, gate off => go to release */ 102 | { SUSTAIN, 1'b1 }: next_state = SUSTAIN; /* sustain, gate on => stay in sustain */ 103 | { RELEASE, 1'b0 }: next_state = OFF; /* release, gate off => end state */ 104 | { RELEASE, 1'b1 }: next_state = ATTACK; /* release, gate on => attack */ 105 | { OFF, 1'b0 }: next_state = OFF; /* end_state, gate off => stay in end state */ 106 | { OFF, 1'b1 }: next_state = ATTACK; /* end_state, gate on => attack */ 107 | default: next_state = OFF; /* default is end (off) state */ 108 | endcase 109 | end 110 | endfunction 111 | 112 | wire overflow; 113 | assign overflow = accumulator[ACCUMULATOR_BITS]; 114 | 115 | reg prev_gate; 116 | 117 | always @(posedge clk) 118 | begin 119 | 120 | /* check for gate low->high transitions (straight to attack phase)*/ 121 | prev_gate <= gate; 122 | if (gate && !prev_gate) 123 | begin 124 | accumulator <= 0; 125 | state <= ATTACK; 126 | end 127 | 128 | /* otherwise, flow through ADSR state machine */ 129 | if (overflow) 130 | begin 131 | accumulator <= 0; 132 | dectmp <= 8'd255; 133 | state <= next_state(state, gate); 134 | end 135 | else begin 136 | case (state) 137 | ATTACK: 138 | begin 139 | accumulator <= accumulator + ATTACK_INC; 140 | amplitude <= accumulator[ACCUMULATOR_BITS-1 -: 8]; 141 | end 142 | DECAY: 143 | begin 144 | accumulator <= accumulator + DECAY_INC; 145 | dectmp <= ((exp_out * SUSTAIN_GAP) >> 8) + SUSTAIN_VOLUME; 146 | amplitude <= dectmp; 147 | end 148 | SUSTAIN: 149 | begin 150 | amplitude <= SUSTAIN_VOLUME; 151 | state <= next_state(state, gate); 152 | end 153 | RELEASE: 154 | begin 155 | accumulator <= accumulator + RELEASE_INC; 156 | reltmp <= ((exp_out * SUSTAIN_VOLUME) >> 8); 157 | amplitude <= reltmp; 158 | if (gate) begin 159 | amplitude <= 0; 160 | accumulator <= 0; 161 | state <= next_state(state, gate); 162 | end 163 | end 164 | default: 165 | begin 166 | amplitude <= 0; 167 | accumulator <= 0; 168 | state <= next_state(state, gate); 169 | end 170 | endcase 171 | end 172 | end 173 | endmodule 174 | 175 | `endif 176 | -------------------------------------------------------------------------------- /examples/midi2/midi_note_to_tone_freq.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MIDI_NOTE_TO_TONE_FREQ__ 2 | `define __TINY_SYNTH_MIDI_NOTE_TO_TONE_FREQ__ 3 | 4 | function [15:0] midi_note_to_tone_freq 5 | ( 6 | input [7:0] midi_note 7 | ); 8 | 9 | case (midi_note) 10 | 8'h00: midi_note_to_tone_freq = 16'h0089; 11 | 8'h01: midi_note_to_tone_freq = 16'h0091; 12 | 8'h02: midi_note_to_tone_freq = 16'h0099; 13 | 8'h03: midi_note_to_tone_freq = 16'h00a3; 14 | 8'h04: midi_note_to_tone_freq = 16'h00ac; 15 | 8'h05: midi_note_to_tone_freq = 16'h00b7; 16 | 8'h06: midi_note_to_tone_freq = 16'h00c1; 17 | 8'h07: midi_note_to_tone_freq = 16'h00cd; 18 | 8'h08: midi_note_to_tone_freq = 16'h00d9; 19 | 8'h09: midi_note_to_tone_freq = 16'h00e6; 20 | 8'h0a: midi_note_to_tone_freq = 16'h00f4; 21 | 8'h0b: midi_note_to_tone_freq = 16'h0102; 22 | 8'h0c: midi_note_to_tone_freq = 16'h0112; 23 | 8'h0d: midi_note_to_tone_freq = 16'h0122; 24 | 8'h0e: midi_note_to_tone_freq = 16'h0133; 25 | 8'h0f: midi_note_to_tone_freq = 16'h0146; 26 | 8'h10: midi_note_to_tone_freq = 16'h0159; 27 | 8'h11: midi_note_to_tone_freq = 16'h016e; 28 | 8'h12: midi_note_to_tone_freq = 16'h0183; 29 | 8'h13: midi_note_to_tone_freq = 16'h019b; 30 | 8'h14: midi_note_to_tone_freq = 16'h01b3; 31 | 8'h15: midi_note_to_tone_freq = 16'h01cd; 32 | 8'h16: midi_note_to_tone_freq = 16'h01e8; 33 | 8'h17: midi_note_to_tone_freq = 16'h0205; 34 | 8'h18: midi_note_to_tone_freq = 16'h0224; 35 | 8'h19: midi_note_to_tone_freq = 16'h0245; 36 | 8'h1a: midi_note_to_tone_freq = 16'h0267; 37 | 8'h1b: midi_note_to_tone_freq = 16'h028c; 38 | 8'h1c: midi_note_to_tone_freq = 16'h02b3; 39 | 8'h1d: midi_note_to_tone_freq = 16'h02dc; 40 | 8'h1e: midi_note_to_tone_freq = 16'h0307; 41 | 8'h1f: midi_note_to_tone_freq = 16'h0336; 42 | 8'h20: midi_note_to_tone_freq = 16'h0366; 43 | 8'h21: midi_note_to_tone_freq = 16'h039a; 44 | 8'h22: midi_note_to_tone_freq = 16'h03d1; 45 | 8'h23: midi_note_to_tone_freq = 16'h040b; 46 | 8'h24: midi_note_to_tone_freq = 16'h0449; 47 | 8'h25: midi_note_to_tone_freq = 16'h048a; 48 | 8'h26: midi_note_to_tone_freq = 16'h04cf; 49 | 8'h27: midi_note_to_tone_freq = 16'h0518; 50 | 8'h28: midi_note_to_tone_freq = 16'h0566; 51 | 8'h29: midi_note_to_tone_freq = 16'h05b8; 52 | 8'h2a: midi_note_to_tone_freq = 16'h060f; 53 | 8'h2b: midi_note_to_tone_freq = 16'h066c; 54 | 8'h2c: midi_note_to_tone_freq = 16'h06cd; 55 | 8'h2d: midi_note_to_tone_freq = 16'h0735; 56 | 8'h2e: midi_note_to_tone_freq = 16'h07a3; 57 | 8'h2f: midi_note_to_tone_freq = 16'h0817; 58 | 8'h30: midi_note_to_tone_freq = 16'h0892; 59 | 8'h31: midi_note_to_tone_freq = 16'h0915; 60 | 8'h32: midi_note_to_tone_freq = 16'h099f; 61 | 8'h33: midi_note_to_tone_freq = 16'h0a31; 62 | 8'h34: midi_note_to_tone_freq = 16'h0acd; 63 | 8'h35: midi_note_to_tone_freq = 16'h0b71; 64 | 8'h36: midi_note_to_tone_freq = 16'h0c1f; 65 | 8'h37: midi_note_to_tone_freq = 16'h0cd8; 66 | 8'h38: midi_note_to_tone_freq = 16'h0d9b; 67 | 8'h39: midi_note_to_tone_freq = 16'h0e6a; 68 | 8'h3a: midi_note_to_tone_freq = 16'h0f46; 69 | 8'h3b: midi_note_to_tone_freq = 16'h102e; 70 | 8'h3c: midi_note_to_tone_freq = 16'h1125; /* middle C */ 71 | 8'h3d: midi_note_to_tone_freq = 16'h122a; 72 | 8'h3e: midi_note_to_tone_freq = 16'h133e; 73 | 8'h3f: midi_note_to_tone_freq = 16'h1463; 74 | 8'h40: midi_note_to_tone_freq = 16'h159a; 75 | 8'h41: midi_note_to_tone_freq = 16'h16e2; 76 | 8'h42: midi_note_to_tone_freq = 16'h183f; 77 | 8'h43: midi_note_to_tone_freq = 16'h19b0; 78 | 8'h44: midi_note_to_tone_freq = 16'h1b37; 79 | 8'h45: midi_note_to_tone_freq = 16'h1cd5; 80 | 8'h46: midi_note_to_tone_freq = 16'h1e8c; 81 | 8'h47: midi_note_to_tone_freq = 16'h205d; 82 | 8'h48: midi_note_to_tone_freq = 16'h224a; 83 | 8'h49: midi_note_to_tone_freq = 16'h2454; 84 | 8'h4a: midi_note_to_tone_freq = 16'h267d; 85 | 8'h4b: midi_note_to_tone_freq = 16'h28c7; 86 | 8'h4c: midi_note_to_tone_freq = 16'h2b34; 87 | 8'h4d: midi_note_to_tone_freq = 16'h2dc5; 88 | 8'h4e: midi_note_to_tone_freq = 16'h307e; 89 | 8'h4f: midi_note_to_tone_freq = 16'h3360; 90 | 8'h50: midi_note_to_tone_freq = 16'h366f; 91 | 8'h51: midi_note_to_tone_freq = 16'h39ab; 92 | 8'h52: midi_note_to_tone_freq = 16'h3d19; 93 | 8'h53: midi_note_to_tone_freq = 16'h40bb; 94 | 8'h54: midi_note_to_tone_freq = 16'h4495; 95 | 8'h55: midi_note_to_tone_freq = 16'h48a8; 96 | 8'h56: midi_note_to_tone_freq = 16'h4cfb; 97 | 8'h57: midi_note_to_tone_freq = 16'h518e; 98 | 8'h58: midi_note_to_tone_freq = 16'h5668; 99 | 8'h59: midi_note_to_tone_freq = 16'h5b8b; 100 | 8'h5a: midi_note_to_tone_freq = 16'h60fd; 101 | 8'h5b: midi_note_to_tone_freq = 16'h66c1; 102 | 8'h5c: midi_note_to_tone_freq = 16'h6cde; 103 | 8'h5d: midi_note_to_tone_freq = 16'h7357; 104 | 8'h5e: midi_note_to_tone_freq = 16'h7a33; 105 | 8'h5f: midi_note_to_tone_freq = 16'h8177; 106 | 8'h60: midi_note_to_tone_freq = 16'h892a; 107 | 8'h61: midi_note_to_tone_freq = 16'h9151; 108 | 8'h62: midi_note_to_tone_freq = 16'h99f6; 109 | 8'h63: midi_note_to_tone_freq = 16'ha31d; 110 | 8'h64: midi_note_to_tone_freq = 16'hacd0; 111 | 8'h65: midi_note_to_tone_freq = 16'hb717; 112 | 8'h66: midi_note_to_tone_freq = 16'hc1fa; 113 | 8'h67: midi_note_to_tone_freq = 16'hcd83; 114 | 8'h68: midi_note_to_tone_freq = 16'hd9bc; 115 | 8'h69: midi_note_to_tone_freq = 16'he6ae; 116 | 8'h6a: midi_note_to_tone_freq = 16'hf466; 117 | default: midi_note_to_tone_freq = 16'hf466; // 16-bit increment counter doesn't have resolution for further notes 118 | // 8'h6b: midi_note_to_tone_freq = 16'h102ee; 119 | // 8'h6c: midi_note_to_tone_freq = 16'h11254; 120 | // 8'h6d: midi_note_to_tone_freq = 16'h122a3; 121 | // 8'h6e: midi_note_to_tone_freq = 16'h133ec; 122 | // 8'h6f: midi_note_to_tone_freq = 16'h1463b; 123 | // 8'h70: midi_note_to_tone_freq = 16'h159a1; 124 | // 8'h71: midi_note_to_tone_freq = 16'h16e2f; 125 | // 8'h72: midi_note_to_tone_freq = 16'h183f5; 126 | // 8'h73: midi_note_to_tone_freq = 16'h19b07; 127 | // 8'h74: midi_note_to_tone_freq = 16'h1b378; 128 | // 8'h75: midi_note_to_tone_freq = 16'h1cd5c; 129 | // 8'h76: midi_note_to_tone_freq = 16'h1e8cc; 130 | // 8'h77: midi_note_to_tone_freq = 16'h205dc; 131 | // 8'h78: midi_note_to_tone_freq = 16'h224a8; 132 | // 8'h79: midi_note_to_tone_freq = 16'h24547; 133 | // 8'h7a: midi_note_to_tone_freq = 16'h267d8; 134 | // 8'h7b: midi_note_to_tone_freq = 16'h28c77; 135 | // 8'h7c: midi_note_to_tone_freq = 16'h2b343; 136 | // 8'h7d: midi_note_to_tone_freq = 16'h2dc5e; 137 | // 8'h7e: midi_note_to_tone_freq = 16'h307ea; 138 | // 8'h7f: midi_note_to_tone_freq = 16'h3360e; 139 | endcase 140 | 141 | endfunction 142 | 143 | `endif 144 | -------------------------------------------------------------------------------- /examples/midi2/midi_uart.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MIDI_UART__ 2 | `define __TINY_SYNTH_MIDI_UART__ 3 | 4 | /* 5 | * A module which converts raw incoming MIDI byte data into events 6 | * with their parameters, ready for further processing. 7 | * 8 | * ______________ 9 | * byte stream | | 24-bit midi events 10 | * ----------------> | MIDI framer | ---------------------> 11 | * |_____________| 12 | * 13 | * MIDI commands have quite a simple structure. 14 | * 15 | * The first (command) byte can be recognised by the fact that has the MSB set. 16 | * 17 | * Valid commands for our purposes range from 0x80 to 0xEF. 18 | * 19 | * Command Meaning # parameters param 1 param 2 20 | * 0x80 Note-off 2 key velocity 21 | * 0x90 Note-on 2 key velocity 22 | * 0xA0 Aftertouch 2 key touch 23 | * 0xB0 Continuous controller 2 controller # controller value 24 | * 0xC0 Patch change 1 instrument # 25 | * 0xD0 Channel Pressure 1 pressure 26 | * 0xE0 Pitch bend 2 lsb (7 bits) msb (7 bits) 27 | * 0xF0 (non-musical commands) 28 | * 29 | * NOTE: MIDI allows a single "command" byte to be followed by multiple 30 | * sets of parameters - the command byte is remembered. 31 | */ 32 | 33 | module midi_uart( 34 | input [7:0] din, /* input raw byte data from MIDI bus */ 35 | input din_clk, /* input clock (pos edge) */ 36 | input clk, 37 | input serial_rx, 38 | output serial_tx, 39 | 40 | input midi_event_ack, 41 | output [7:0] midi_command, /* output: MIDI command byte */ 42 | output [6:0] midi_parameter_1, /* output: MIDI parameter #1 */ 43 | output [6:0] midi_parameter_2, /* output: MIDI parameter #2 */ 44 | output reg midi_event_valid /* output: MIDI command and parameters contain valid data */ 45 | ); 46 | 47 | reg [7:0] uart_rx_data; 48 | reg uart_read_ack; 49 | reg uart_recv_data_valid; 50 | 51 | reg uart_tx_busy; /* not used */ 52 | 53 | 54 | reg [3:0] state; /* number of MIDI bytes received (including command) */ 55 | reg [21:0] in_flight_midi_message; 56 | reg [2:0] in_flight_expected_param_count; 57 | 58 | simpleuart #(.CLOCK_FREQUENCY(16000000), .BAUD_RATE(31250)) uart( 59 | .clk(clk), .resetn(1'b1), 60 | .ser_rx(serial_rx), 61 | .reg_dat_re(uart_read_ack), 62 | .reg_dat_do(uart_rx_data), 63 | .recv_buf_valid(uart_recv_data_valid), 64 | 65 | /* we never write to the UART */ 66 | .ser_tx(serial_tx), 67 | .reg_dat_we(1'b0), 68 | .reg_dat_di(8'd0), 69 | .tx_busy(uart_tx_busy) 70 | ); 71 | 72 | initial begin 73 | midi_event_valid <= 1'b0; 74 | midi_command <= 8'h00; 75 | midi_parameter_1 <= 7'h00; 76 | midi_parameter_2 <= 7'h00; 77 | 78 | in_flight_expected_param_count <= 2'h0; 79 | end 80 | 81 | function [2:0] expected_midi_parameter_count( 82 | input [3:0] command 83 | ); 84 | begin 85 | case (command) 86 | 4'hc: expected_midi_parameter_count = 3'd1; /* patch change */ 87 | 4'hd: expected_midi_parameter_count = 3'd1; /* channel pressure */ 88 | default: expected_midi_parameter_count = 3'd2; 89 | endcase 90 | end 91 | endfunction 92 | 93 | always @(posedge clk) 94 | begin 95 | if (midi_event_ack) begin 96 | midi_event_valid <= 0; 97 | end 98 | 99 | if (uart_recv_data_valid && !uart_read_ack) begin 100 | // we've just read a new byte from the UART 101 | if (uart_rx_data[7:4] == 4'hf) begin 102 | state <= 4'hf; 103 | end else if (uart_rx_data[7] === 1'b1) begin 104 | // the MSB was set, so this signifies a new MIDI "command" 105 | state <= 1; 106 | in_flight_expected_param_count <= expected_midi_parameter_count(uart_rx_data[7:4]); 107 | in_flight_midi_message <= { uart_rx_data, 14'b0 }; 108 | end else begin 109 | // MSB was not set, so this is a parameter byte 110 | case (state) 111 | 1: begin // waiting for parameter #1 to arrive 112 | if (in_flight_expected_param_count == 1) 113 | begin 114 | // if we only wanted one parameter, then we've got it, so 115 | // clock this out as a valid MIDI event 116 | midi_command <= in_flight_midi_message[21:14]; 117 | midi_parameter_1 <= uart_rx_data; 118 | midi_parameter_2 <= 7'b0; 119 | midi_event_valid <= 1'b1; 120 | state <= 1; 121 | end else begin 122 | // our MIDI command expected more than one parameter, 123 | // so we need to wait for the next to arrive. 124 | in_flight_midi_message <= { in_flight_midi_message[21:14], uart_rx_data[6:0], 7'b0 }; 125 | state <= state + 1; 126 | end 127 | end 128 | 2: begin // waiting for parameter #2 to arrive 129 | if (in_flight_expected_param_count == 2) 130 | begin 131 | // we were expecting two parameters, we've got them, 132 | // so clock them out as a valid MIDI event 133 | midi_command <= in_flight_midi_message[21:14]; 134 | midi_parameter_1 <= in_flight_midi_message[13:7]; 135 | midi_parameter_2 <= uart_rx_data[6:0]; 136 | midi_event_valid <= 1'b1; 137 | end 138 | // we don't support commands with more than 2 parameters, 139 | // so now we transition to a "null" state, until the next 140 | // command arrives. 141 | state <= 1; 142 | end 143 | default: begin 144 | state <= 4'hf; 145 | end 146 | endcase 147 | end 148 | 149 | uart_read_ack <= 1'b1; 150 | end else begin 151 | uart_read_ack <= 1'b0; 152 | end 153 | 154 | end 155 | 156 | endmodule 157 | 158 | `endif 159 | -------------------------------------------------------------------------------- /examples/midi2/pins.pcf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # TinyFPGA BX constraint file (.pcf) 4 | # 5 | ############################################################################### 6 | # 7 | # Copyright (c) 2018, Luke Valenty 8 | # All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | # 13 | # 1. Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | # 2. Redistributions in binary form must reproduce the above copyright notice, 16 | # this list of conditions and the following disclaimer in the documentation 17 | # and/or other materials provided with the distribution. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | # 30 | # The views and conclusions contained in the software and documentation are those 31 | # of the authors and should not be interpreted as representing official policies, 32 | # either expressed or implied, of the project. 33 | # 34 | ############################################################################### 35 | 36 | #### 37 | # TinyFPGA BX information: https://github.com/tinyfpga/TinyFPGA-BX/ 38 | #### 39 | 40 | # Left side of board 41 | set_io --warn-no-port PIN_1 A2 42 | #set_io --warn-no-port PIN_2 A1 43 | #set_io --warn-no-port PIN_3 B1 44 | #set_io --warn-no-port PIN_4 C2 45 | #set_io --warn-no-port PIN_5 C1 46 | #set_io --warn-no-port PIN_6 D2 47 | #set_io --warn-no-port PIN_7 D1 48 | #set_io --warn-no-port PIN_8 E2 49 | #set_io --warn-no-port PIN_9 E1 50 | #set_io --warn-no-port PIN_10 G2 51 | #set_io --warn-no-port PIN_11 H1 52 | #set_io --warn-no-port PIN_12 J1 53 | #set_io --warn-no-port PIN_13 H2 54 | 55 | # Right side of board 56 | set_io --warn-no-port PIN_14 H9 57 | set_io --warn-no-port PIN_15 D9 58 | #set_io --warn-no-port PIN_16 D8 59 | #set_io --warn-no-port PIN_17 C9 60 | #set_io --warn-no-port PIN_18 A9 61 | #set_io --warn-no-port PIN_19 B8 62 | #set_io --warn-no-port PIN_20 A8 63 | #set_io --warn-no-port PIN_21 B7 64 | #set_io --warn-no-port PIN_22 A7 65 | #set_io --warn-no-port PIN_23 B6 66 | #set_io --warn-no-port PIN_24 A6 67 | 68 | # SPI flash interface on bottom of board 69 | #set_io --warn-no-port SPI_SS F7 70 | #set_io --warn-no-port SPI_SCK G7 71 | #set_io --warn-no-port SPI_IO0 G6 72 | #set_io --warn-no-port SPI_IO1 H7 73 | #set_io --warn-no-port SPI_IO2 H4 74 | #set_io --warn-no-port SPI_IO3 J8 75 | 76 | # General purpose pins on bottom of board 77 | #set_io --warn-no-port PIN_25 G1 78 | #set_io --warn-no-port PIN_26 J3 79 | #set_io --warn-no-port PIN_27 J4 80 | #set_io --warn-no-port PIN_28 G9 81 | #set_io --warn-no-port PIN_29 J9 82 | #set_io --warn-no-port PIN_30 E8 83 | #set_io --warn-no-port PIN_31 J2 84 | 85 | # LED 86 | #set_io --warn-no-port LED B3 87 | 88 | # USB 89 | #set_io --warn-no-port USBP B4 90 | #set_io --warn-no-port USBN A4 91 | set_io --warn-no-port USBPU A3 92 | 93 | # 16MHz clock 94 | set_io --warn-no-port CLK B2 # input 95 | 96 | set_io SWITCHES[0] D8 97 | set_io SWITCHES[1] C9 98 | set_io SWITCHES[2] A9 99 | set_io SWITCHES[3] B8 100 | 101 | -------------------------------------------------------------------------------- /examples/midi2/simpleuart.vh: -------------------------------------------------------------------------------- 1 | /* 2 | * PicoSoC - A simple example SoC using PicoRV32 3 | * 4 | * Copyright (C) 2017 Clifford Wolf 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | */ 19 | 20 | /* 21 | * Some minor modifications have been made to Clifford's code to remove 22 | * some of the SoC integration complexities - I've also commented things 23 | * for my own understanding. 24 | * 25 | */ 26 | `ifndef __PICOSOC_SIMPLEUART__ 27 | `define __PICOSOC_SIMPLEUART__ 28 | 29 | module simpleuart #( 30 | parameter CLOCK_FREQUENCY = 16000000, 31 | parameter BAUD_RATE = 115200 32 | ) ( 33 | input clk, 34 | input resetn, /* active low reset */ 35 | 36 | output ser_tx, /* serial transmit out */ 37 | input ser_rx, /* serial receive in */ 38 | 39 | output recv_buf_valid, 40 | input reg_dat_re, 41 | output [7:0] reg_dat_do, 42 | 43 | input reg_dat_we, 44 | input [7:0] reg_dat_di, 45 | output tx_busy); 46 | 47 | localparam cfg_divider = $rtoi(CLOCK_FREQUENCY / BAUD_RATE); 48 | 49 | /* 50 | * RECEIVE related registers 51 | */ 52 | 53 | /* =============================================================== 54 | * TABLE OF RECEIVE STATES 55 | * =============================================================== 56 | * 57 | * STATE | DESCRIPTION 58 | * ------+------------------------- 59 | * 0 | waiting for start bit 60 | * 1 | start bit detected, waiting for middle of start bit 61 | * 2 | waiting for middle of bit 0 62 | * 3 | waiting for middle of bit 1 63 | * 4 | waiting for middle of bit 2 64 | * 5 | waiting for middle of bit 3 65 | * 6 | waiting for middle of bit 4 66 | * 7 | waiting for middle of bit 5 67 | * 8 | waiting for middle of bit 6 68 | * 9 | waiting for middle of bit 7 69 | * 10 | waiting for middle of stop bit 70 | */ 71 | reg [3:0] recv_state; /* recv_state is essentially a bit counter - indicating which bit the receiver is expecting */ 72 | 73 | reg [31:0] recv_divcnt; /* up-counter, to count cycles for current bit */ 74 | reg [7:0] recv_pattern; /* currently received pattern; clocked in from MSB end */ 75 | reg [7:0] recv_buf_data; /* previously received byte */ 76 | reg recv_buf_valid; /* flag to indicate that the buffered receive byte is valid, and can be clocked out with reg_dat_re */ 77 | 78 | 79 | /* data out line is either data from the buffer (if the data is available and valid), 80 | * or it's 0xff otherwise. 81 | */ 82 | assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0; 83 | assign tx_busy = reg_dat_we && (send_bitcnt || send_dummy); 84 | 85 | /* ===================================================================================================================== */ 86 | 87 | /* 88 | * TRANSMIT related registers 89 | */ 90 | reg [9:0] send_pattern; /* current pattern being clocked out on the serial line */ 91 | reg [3:0] send_bitcnt; /* number of bits remaining to be sent */ 92 | reg [31:0] send_divcnt; /* number of clock cycles counted for the current bit */ 93 | reg send_dummy; /* flag to indicate that the next "slot" is empty */ 94 | 95 | /* reg_dat_wait is asserted when an attempted write is not possible because 96 | * the serial line is busy because it's either currently sending data 97 | * (send_bitcnt is non-zero) or is about to send a dummy slot (send_dummy is 1). 98 | */ 99 | assign tx_busy = reg_dat_we && (send_bitcnt || send_dummy); 100 | 101 | always @(posedge clk) begin 102 | if (!resetn) begin 103 | recv_state <= 0; 104 | recv_divcnt <= 0; 105 | recv_pattern <= 0; 106 | recv_buf_data <= 0; 107 | recv_buf_valid <= 0; 108 | end else begin 109 | recv_divcnt <= recv_divcnt + 1; 110 | 111 | /* if data has been clocked out, reset buf valid flag to 0 */ 112 | if (reg_dat_re) 113 | recv_buf_valid <= 0; 114 | 115 | case (recv_state) 116 | 0: begin 117 | /* if our serial input has been brought low, we're now 118 | * expecting the start bit */ 119 | if (!ser_rx) 120 | recv_state <= 1; 121 | 122 | recv_divcnt <= 0; 123 | end 124 | 1: begin 125 | /* state 1 means we're waiting for the middle of the start bit. */ 126 | if (ser_rx) begin 127 | // we had a false start bit; reset and try again 128 | recv_state <= 0; 129 | end else begin 130 | if (2*recv_divcnt > cfg_divider) begin 131 | /* at this point we've aligned ourselves half-way 132 | * through the start-bit (notice the 2x multiplier above), 133 | * so we reset the counter to zero, and wait for the next 134 | * full-bit cycle(s) so we can sample the middle of the incoming bits. 135 | */ 136 | recv_state <= 2; 137 | recv_divcnt <= 0; 138 | end 139 | end 140 | end 141 | 10: begin 142 | if (recv_divcnt > cfg_divider) begin 143 | /* at this point we're in the middle of receiving the 144 | * stop bit, and rather than doing anything with it, we 145 | * use this as an opportunity to clock out the newly 146 | * received byte by setting the recv_buf_valid flag, 147 | * and then bounce back to state 0. (note: stop-bit is 148 | * logic-level high, so we're ready to look for the next 149 | * high-low transition to signal the next bit). 150 | */ 151 | recv_buf_data <= recv_pattern; 152 | recv_buf_valid <= 1; 153 | recv_state <= 0; 154 | end 155 | end 156 | default: begin 157 | /* states 2-9; clocking in bits 0-7. */ 158 | if (recv_divcnt > cfg_divider) begin 159 | recv_pattern <= {ser_rx, recv_pattern[7:1]}; 160 | recv_state <= recv_state + 1; 161 | recv_divcnt <= 0; 162 | end 163 | end 164 | endcase 165 | end 166 | end 167 | 168 | /* ==================================================================== 169 | * TRANSMIT LOGIC 170 | * ==================================================================== 171 | */ 172 | 173 | // clock out bit zero of send_pattern 174 | assign ser_tx = send_pattern[0]; 175 | 176 | always @(posedge clk) begin 177 | send_divcnt <= send_divcnt + 1; 178 | if (!resetn) begin 179 | /* reset line low, so reset all signals */ 180 | send_pattern <= ~0; 181 | send_bitcnt <= 0; 182 | send_divcnt <= 0; 183 | send_dummy <= 1; 184 | end else begin 185 | if (send_dummy && !send_bitcnt) begin 186 | /* send_dummy requested for next timing slot, 187 | * reset bit count to 15, set send pattern to all 1's, 188 | * clear send_dummy flag */ 189 | send_pattern <= ~0; 190 | send_bitcnt <= 15; 191 | send_divcnt <= 0; 192 | send_dummy <= 0; 193 | end else 194 | if (reg_dat_we && !send_bitcnt) begin 195 | /* write-enable set, and we're not in the process of sending 196 | * any bits, so pull the send_pattern from our data-input port, 197 | * (surrounded by start and stop bits) 198 | */ 199 | send_pattern <= {1'b1, reg_dat_di, 1'b0}; 200 | send_bitcnt <= 10; 201 | send_divcnt <= 0; 202 | end else 203 | if (send_divcnt > cfg_divider && send_bitcnt) begin 204 | /* 205 | * we're ready for the next bit, so rotate send_pattern one 206 | * bit to the right (and fill MSB with a 1). Also 207 | * decrement the number of bits left to send, and 208 | * reset the counter. 209 | */ 210 | send_pattern <= {1'b1, send_pattern[9:1]}; 211 | send_bitcnt <= send_bitcnt - 1; 212 | send_divcnt <= 0; 213 | end 214 | end 215 | end 216 | endmodule 217 | 218 | `endif 219 | -------------------------------------------------------------------------------- /examples/midi2/top.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny-synth example: playing notes based on MIDI input 3 | */ 4 | 5 | `define __TINY_SYNTH_ROOT_FOLDER "../.." 6 | `include "../../hdl/tiny-synth-all.vh" 7 | `include "midi_player.vh" 8 | 9 | // look in pins.pcf for all the pin names on the TinyFPGA BX board 10 | module top ( 11 | `ifdef blackice 12 | input CLK_100, // 100MHz clock 13 | `else 14 | input CLK, // 16MHz clock 15 | `endif 16 | output USBPU, // USB pull-up resistor 17 | input [3:0] SWITCHES, 18 | input PIN_14, // serial (MIDI) data in 19 | output PIN_15, // serial (MIDI) data out 20 | output PIN_1); /* audio out */ 21 | 22 | `ifdef blackice 23 | wire CLK; 24 | pll pll0 (.clock_in(CLK_100), .clock_out(CLK)); 25 | `endif 26 | 27 | // drive USB pull-up resistor to '0' to disable USB 28 | assign USBPU = 0; 29 | 30 | wire serial_rx; 31 | SB_IO #( 32 | .PIN_TYPE(6'b0000_01), 33 | .PULLUP(1'b0) 34 | ) serial_rx_pin_conf ( 35 | .PACKAGE_PIN(PIN_14), 36 | .D_IN_0(serial_rx) 37 | ); 38 | 39 | localparam SAMPLE_BITS = 12; 40 | 41 | wire signed [SAMPLE_BITS-1:0] final_mix; 42 | midi_player #(.SAMPLE_BITS(SAMPLE_BITS)) midi_player(.clk(CLK), .serial_rx(serial_rx), .serial_tx(PIN_15), .audio_data(final_mix), .waveform(SWITCHES)); 43 | 44 | pdm_dac #(.DATA_BITS(SAMPLE_BITS)) dac1( 45 | .din(final_mix), 46 | .clk(CLK), // DAC runs at full 16MHz speed. 47 | .dout(PIN_1) 48 | ); 49 | 50 | endmodule 51 | -------------------------------------------------------------------------------- /examples/midi2/voice_fixed_param.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_VOICE_FIXED_PARAM__ 2 | `define __TINY_SYNTH_VOICE_FIXED_PARAM__ 3 | 4 | `include "../../hdl/tone_generator.vh" 5 | `include "envelope_generator_fixed_param.vh" 6 | `include "../../hdl/amplitude_modulator.vh" 7 | 8 | module voice_fixed_param #( 9 | parameter OUTPUT_BITS = 12, 10 | parameter FREQ_BITS = 16, 11 | parameter PULSEWIDTH_BITS = 12, 12 | parameter ACCUMULATOR_BITS = 24, 13 | parameter SAMPLE_CLK_FREQ = 44100, 14 | parameter ATTACK_INC = 1000, 15 | parameter DECAY_INC = 1000, 16 | parameter [7:0] SUSTAIN_VOLUME = 128, 17 | parameter RELEASE_INC = 1000 18 | )( 19 | input [FREQ_BITS-1:0] tone_freq, 20 | input [PULSEWIDTH_BITS-1:0] pulse_width, 21 | input main_clk, 22 | input sample_clk, 23 | input rst, 24 | input [3:0] waveform, 25 | output wire signed [OUTPUT_BITS-1:0] dout, 26 | output wire accumulator_msb, /* used to feed ringmod on another voice */ 27 | output wire accumulator_overflow, /* set when accumulator = 0; used to sync with another oscillator */ 28 | output wire is_idle, 29 | input en_ringmod, 30 | input wire ringmod_source, 31 | input en_sync, 32 | input wire sync_source, 33 | 34 | // envelope generator params 35 | input wire gate 36 | ); 37 | 38 | wire signed [OUTPUT_BITS-1:0] tone_generator_data; 39 | wire[7:0] envelope_amplitude; 40 | 41 | tone_generator #( 42 | .FREQ_BITS(FREQ_BITS), 43 | .PULSEWIDTH_BITS(PULSEWIDTH_BITS), 44 | .OUTPUT_BITS(OUTPUT_BITS), 45 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 46 | ) tone_generator ( 47 | .tone_freq(tone_freq), 48 | .pulse_width(pulse_width), 49 | .main_clk(main_clk), 50 | .sample_clk(sample_clk), 51 | .rst(rst), 52 | .dout(tone_generator_data), 53 | .accumulator_msb(accumulator_msb), 54 | .accumulator_overflow(accumulator_overflow), 55 | .en_sync(en_sync), 56 | .sync_source(sync_source), 57 | .en_ringmod(en_ringmod), 58 | .ringmod_source(ringmod_source), 59 | .en_noise(waveform[0]), 60 | .en_pulse(waveform[1]), 61 | .en_triangle(waveform[2]), 62 | .en_saw(waveform[3]) 63 | ); 64 | 65 | envelope_generator_fixed_param #( 66 | .SAMPLE_CLK_FREQ(SAMPLE_CLK_FREQ), 67 | .ATTACK_INC(ATTACK_INC), 68 | .DECAY_INC(DECAY_INC), 69 | .SUSTAIN_VOLUME(SUSTAIN_VOLUME), 70 | .RELEASE_INC(RELEASE_INC) 71 | ) envelope( 72 | .clk(sample_clk), 73 | .rst(rst), 74 | .gate(gate), 75 | .is_idle(is_idle), 76 | .amplitude(envelope_amplitude) 77 | ); 78 | 79 | amplitude_modulator #(.DATA_BITS(OUTPUT_BITS)) modulator( 80 | .clk(sample_clk), 81 | .din(tone_generator_data), 82 | .amplitude(envelope_amplitude), 83 | .dout(dout) 84 | ); 85 | 86 | endmodule 87 | 88 | `endif 89 | -------------------------------------------------------------------------------- /examples/midi_voice_control/Makefile: -------------------------------------------------------------------------------- 1 | VERILOG_FILES=top.v ../../blackice//pll.v 2 | PCF_FILE = ../../blackice/blackice.pcf 3 | 4 | include ../../blackice/blackice.mk 5 | -------------------------------------------------------------------------------- /examples/midi_voice_control/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | A simple MIDI-based example for tiny-synth. 4 | 5 | This example is a four-voice MIDI-connected synthesizer. 6 | 7 | This example includes support for controlling MIDI parameters using 8 | the general-purpose controllers that some MIDI keyboards have. 9 | 10 | My keyboard is made by Roland, and has controller knobs which send messages of the format: 11 | 12 | `Bc aa bb` 13 | 14 | Where `c` is the channel number (usually 0), `aa` is the controller number (`0x0e` -> `0x15` for the 8 knobs I have available from left-to-right), and `bb` is a value from `0x00` -> `0x7f` which represents the value that has been set. 15 | 16 | According to the notes [here](http://www.indiana.edu/~emusic/etext/MIDI/chapter3_MIDI6.shtml), that covers a range that includes the general purpose controllers (`0x10` -> `0x13`), and some "undefined" controllers. 17 | 18 | I've chosen to map the values to knobs that work well for me, with my keyboard, but please feel free to check what messages your equipment sends, and adjust the mappings accordingly. 19 | 20 | ## Principle of operation 21 | 22 | The MIDI interface uses Clifford Wolf's simpleuart code (slightly modified so that it could be used outside of the PicoSoC environment) to read data from the MIDI interface. 23 | 24 | `simpleuart` is wrapped with the `midi_uart` module, which essentially waits for whole MIDI frames (command+parameter messages) to arrive before passing them up to the next layer. 25 | 26 | `midi_player` instantiates 8 voices, and keeps track which voices are available, and which are busy playing. When a "note-on" MIDI message arrives, the first available voice is chosen, and the voice is assigned / gated appropriately. "note-off" messages simply find all voices playing the correct note, and gate them off. 27 | 28 | # Before you start 29 | 30 | This example requires a bit of extra circuitry - namely MIDI input requires a H11L1 opto-coupler to isolate your instrument, and to help prevent ground loops as per the MIDI specification. 31 | 32 | ## Audio output 33 | 34 | As with the other tiny-synth examples, audio-out requires some analog circuitry: 35 | 36 | 37 | The below circuit can be used to filter the output from `PIN_1`. 38 | 39 | ``` 40 | e.g. 330 ohm eg. 10uF 41 | PIN_1 >---./\/\/\.---o------| |-------> Analog out >-- 42 | | 43 | --- 44 | --- eg. 0.1uF 45 | | 46 | | 47 | --- GND 48 | - 49 | ``` 50 | 51 | ## MIDI input 52 | 53 | MIDI input is via a 5-pin DIN MIDI connector (I used a breadboard-friendly one), 54 | and an H11L1 opto-coupler, with a few passives. 55 | 56 | The circuit I used is described below. 57 | 58 | ### MIDI connector pinout 59 | 60 | The pins in the MIDI connector are numbered as below. Imagine this is a female 61 | DIN socket, and you're looking at it head-on. 62 | 63 | ``` 64 | 65 | 5-PIN MIDI female socket; looking head-on 66 | 67 | - '|_|' - 68 | ' ' 69 | | 1 3 | 70 | . 4 5 . 71 | . 2 . 72 | `-----' 73 | 74 | ``` 75 | 76 | ### Circuit diagram 77 | 78 | ``` 79 | ___ GND 80 | | 81 | H11L1 --- 1uF 82 | _____________________ --- 83 | 220ohm 1 | | 6 | 84 | MIDI_IN PIN4 -------./\/\.----o-----------|---. ----.o--|-------o----------o-----------> 3v3 85 | _|_ | _|_ | . | 4 \ 86 | 1N4148 /_\ | \ / '' | \=\ |o-|----------. / 270ohm 87 | | 2 | ~~~ | . | 5 | \ 88 | MIDI_IN PIN5 -----------------o-----------|---' ----'o--|----. `-------o-----------> UART RX (PIN 14 on TinyFPGA BX) 89 | |___________________| | 90 | --- GND 91 | ``` 92 | -------------------------------------------------------------------------------- /examples/midi_voice_control/apio.ini: -------------------------------------------------------------------------------- 1 | [env] 2 | board = TinyFPGA-BX 3 | 4 | -------------------------------------------------------------------------------- /examples/midi_voice_control/f_table_44100Hz.mem: -------------------------------------------------------------------------------- 1 | // F lookup table for use with state variable filter. 2 | // (00..7f maps to filter F value in defined range), 3 | // generated with gen_f_table.py with parameters: 4 | // 5 | // Fs = 44100Hz 6 | // Fmin = 30Hz 7 | // Fmax = 9000Hz 8 | // 9 | 00230 10 | 00247 11 | 00260 12 | 0027a 13 | 00295 14 | 002b1 15 | 002ce 16 | 002ec 17 | 0030c 18 | 0032d 19 | 0034f 20 | 00373 21 | 00398 22 | 003bf 23 | 003e8 24 | 00412 25 | 0043e 26 | 0046c 27 | 0049c 28 | 004ce 29 | 00502 30 | 00538 31 | 00570 32 | 005ab 33 | 005e8 34 | 00628 35 | 0066b 36 | 006b0 37 | 006f9 38 | 00744 39 | 00793 40 | 007e5 41 | 0083a 42 | 00893 43 | 008f0 44 | 00951 45 | 009b6 46 | 00a1f 47 | 00a8c 48 | 00afe 49 | 00b75 50 | 00bf1 51 | 00c73 52 | 00cf9 53 | 00d86 54 | 00e18 55 | 00eb0 56 | 00f4f 57 | 00ff5 58 | 010a2 59 | 01156 60 | 01211 61 | 012d5 62 | 013a0 63 | 01475 64 | 01552 65 | 01639 66 | 01729 67 | 01823 68 | 01929 69 | 01a39 70 | 01b54 71 | 01c7c 72 | 01db0 73 | 01ef1 74 | 02040 75 | 0219d 76 | 02308 77 | 02483 78 | 0260e 79 | 027aa 80 | 02956 81 | 02b15 82 | 02ce7 83 | 02ecd 84 | 030c7 85 | 032d6 86 | 034fc 87 | 03738 88 | 0398d 89 | 03bfb 90 | 03e83 91 | 04127 92 | 043e7 93 | 046c4 94 | 049c0 95 | 04cdd 96 | 0501b 97 | 0537c 98 | 05701 99 | 05aac 100 | 05e7e 101 | 06279 102 | 0669f 103 | 06af1 104 | 06f72 105 | 07422 106 | 07905 107 | 07e1c 108 | 08369 109 | 088ee 110 | 08eae 111 | 094ab 112 | 09ae8 113 | 0a166 114 | 0a829 115 | 0af32 116 | 0b686 117 | 0be26 118 | 0c616 119 | 0ce58 120 | 0d6f0 121 | 0dfe0 122 | 0e92d 123 | 0f2d8 124 | 0fce6 125 | 10759 126 | 11236 127 | 11d7f 128 | 12937 129 | 13564 130 | 14206 131 | 14f23 132 | 15cbd 133 | 16ad7 134 | 17974 135 | 18898 136 | 19844 137 | -------------------------------------------------------------------------------- /examples/midi_voice_control/filter_tables.vh: -------------------------------------------------------------------------------- 1 | `ifndef __FILTER_TABLES__ 2 | `define __FILTER_TABLES__ 3 | 4 | module f_table( 5 | input clk, 6 | input [6:0] val, 7 | output reg signed [17:0] result); 8 | 9 | reg[17:0] LOOKUP_TABLE[0:127]; 10 | initial $readmemh ("f_table_44100Hz.mem", LOOKUP_TABLE); 11 | 12 | always @(posedge clk) begin 13 | result <= LOOKUP_TABLE[val]; 14 | end 15 | endmodule 16 | 17 | module q1_table( 18 | input clk, 19 | input [6:0] val, 20 | output reg signed [17:0] result); 21 | 22 | reg[17:0] LOOKUP_TABLE[0:127]; 23 | initial $readmemh ("q1_table.mem", LOOKUP_TABLE); 24 | 25 | always @(posedge clk) begin 26 | result <= LOOKUP_TABLE[val]; 27 | end 28 | endmodule 29 | 30 | 31 | `endif 32 | -------------------------------------------------------------------------------- /examples/midi_voice_control/gen_f_table.py: -------------------------------------------------------------------------------- 1 | # generate a table of F values for the state variable filter 2 | # MIDI controllers produce values from 00..7f, and this gets mapped 3 | # to a logarithmic range from Fmin to Fmax 4 | 5 | import math 6 | 7 | # sample rate 8 | Fs = 44100 9 | 10 | # min F 11 | Fmin = 30 12 | 13 | # max F 14 | Fmax = 6000 15 | 16 | # number of entries in table 17 | Ts = 128.0 18 | 19 | 20 | 21 | def svf_f(fs, fc): 22 | return 2*math.sin(3.14159265359*(fc/fs)) 23 | 24 | 25 | Frange = Fmax/Fmin; 26 | 27 | table = range(int(Ts)) 28 | table = [ int(svf_f(Fs, (math.pow(Frange, (i/128.0))*Fmin) )*131072.0) for i in table ] 29 | 30 | for i in table: 31 | print( format(i,'05x') ) 32 | -------------------------------------------------------------------------------- /examples/midi_voice_control/gen_q1_table.py: -------------------------------------------------------------------------------- 1 | # generate a table of Q1 values for the state variable filter 2 | # MIDI controllers produce values from 00..7f, and this gets mapped 3 | # linearly to a Q range from Qmin .. Qmax 4 | 5 | Qmin = 0.7 6 | Qmax = 4.0 7 | Ts = 128.0 8 | 9 | table = range(int(Ts)) 10 | 11 | table = [ int((1.0/(Qmin + ((Qmax - Qmin) * (i/Ts)))) * 65536.0) for i in table ] 12 | 13 | for i in table: 14 | print( format(i,'05x') ) 15 | -------------------------------------------------------------------------------- /examples/midi_voice_control/midi_note_to_tone_freq.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MIDI_NOTE_TO_TONE_FREQ__ 2 | `define __TINY_SYNTH_MIDI_NOTE_TO_TONE_FREQ__ 3 | 4 | function [15:0] midi_note_to_tone_freq 5 | ( 6 | input [7:0] midi_note 7 | ); 8 | 9 | case (midi_note) 10 | 8'h00: midi_note_to_tone_freq = 16'h0089; 11 | 8'h01: midi_note_to_tone_freq = 16'h0091; 12 | 8'h02: midi_note_to_tone_freq = 16'h0099; 13 | 8'h03: midi_note_to_tone_freq = 16'h00a3; 14 | 8'h04: midi_note_to_tone_freq = 16'h00ac; 15 | 8'h05: midi_note_to_tone_freq = 16'h00b7; 16 | 8'h06: midi_note_to_tone_freq = 16'h00c1; 17 | 8'h07: midi_note_to_tone_freq = 16'h00cd; 18 | 8'h08: midi_note_to_tone_freq = 16'h00d9; 19 | 8'h09: midi_note_to_tone_freq = 16'h00e6; 20 | 8'h0a: midi_note_to_tone_freq = 16'h00f4; 21 | 8'h0b: midi_note_to_tone_freq = 16'h0102; 22 | 8'h0c: midi_note_to_tone_freq = 16'h0112; 23 | 8'h0d: midi_note_to_tone_freq = 16'h0122; 24 | 8'h0e: midi_note_to_tone_freq = 16'h0133; 25 | 8'h0f: midi_note_to_tone_freq = 16'h0146; 26 | 8'h10: midi_note_to_tone_freq = 16'h0159; 27 | 8'h11: midi_note_to_tone_freq = 16'h016e; 28 | 8'h12: midi_note_to_tone_freq = 16'h0183; 29 | 8'h13: midi_note_to_tone_freq = 16'h019b; 30 | 8'h14: midi_note_to_tone_freq = 16'h01b3; 31 | 8'h15: midi_note_to_tone_freq = 16'h01cd; 32 | 8'h16: midi_note_to_tone_freq = 16'h01e8; 33 | 8'h17: midi_note_to_tone_freq = 16'h0205; 34 | 8'h18: midi_note_to_tone_freq = 16'h0224; 35 | 8'h19: midi_note_to_tone_freq = 16'h0245; 36 | 8'h1a: midi_note_to_tone_freq = 16'h0267; 37 | 8'h1b: midi_note_to_tone_freq = 16'h028c; 38 | 8'h1c: midi_note_to_tone_freq = 16'h02b3; 39 | 8'h1d: midi_note_to_tone_freq = 16'h02dc; 40 | 8'h1e: midi_note_to_tone_freq = 16'h0307; 41 | 8'h1f: midi_note_to_tone_freq = 16'h0336; 42 | 8'h20: midi_note_to_tone_freq = 16'h0366; 43 | 8'h21: midi_note_to_tone_freq = 16'h039a; 44 | 8'h22: midi_note_to_tone_freq = 16'h03d1; 45 | 8'h23: midi_note_to_tone_freq = 16'h040b; 46 | 8'h24: midi_note_to_tone_freq = 16'h0449; 47 | 8'h25: midi_note_to_tone_freq = 16'h048a; 48 | 8'h26: midi_note_to_tone_freq = 16'h04cf; 49 | 8'h27: midi_note_to_tone_freq = 16'h0518; 50 | 8'h28: midi_note_to_tone_freq = 16'h0566; 51 | 8'h29: midi_note_to_tone_freq = 16'h05b8; 52 | 8'h2a: midi_note_to_tone_freq = 16'h060f; 53 | 8'h2b: midi_note_to_tone_freq = 16'h066c; 54 | 8'h2c: midi_note_to_tone_freq = 16'h06cd; 55 | 8'h2d: midi_note_to_tone_freq = 16'h0735; 56 | 8'h2e: midi_note_to_tone_freq = 16'h07a3; 57 | 8'h2f: midi_note_to_tone_freq = 16'h0817; 58 | 8'h30: midi_note_to_tone_freq = 16'h0892; 59 | 8'h31: midi_note_to_tone_freq = 16'h0915; 60 | 8'h32: midi_note_to_tone_freq = 16'h099f; 61 | 8'h33: midi_note_to_tone_freq = 16'h0a31; 62 | 8'h34: midi_note_to_tone_freq = 16'h0acd; 63 | 8'h35: midi_note_to_tone_freq = 16'h0b71; 64 | 8'h36: midi_note_to_tone_freq = 16'h0c1f; 65 | 8'h37: midi_note_to_tone_freq = 16'h0cd8; 66 | 8'h38: midi_note_to_tone_freq = 16'h0d9b; 67 | 8'h39: midi_note_to_tone_freq = 16'h0e6a; 68 | 8'h3a: midi_note_to_tone_freq = 16'h0f46; 69 | 8'h3b: midi_note_to_tone_freq = 16'h102e; 70 | 8'h3c: midi_note_to_tone_freq = 16'h1125; /* middle C */ 71 | 8'h3d: midi_note_to_tone_freq = 16'h122a; 72 | 8'h3e: midi_note_to_tone_freq = 16'h133e; 73 | 8'h3f: midi_note_to_tone_freq = 16'h1463; 74 | 8'h40: midi_note_to_tone_freq = 16'h159a; 75 | 8'h41: midi_note_to_tone_freq = 16'h16e2; 76 | 8'h42: midi_note_to_tone_freq = 16'h183f; 77 | 8'h43: midi_note_to_tone_freq = 16'h19b0; 78 | 8'h44: midi_note_to_tone_freq = 16'h1b37; 79 | 8'h45: midi_note_to_tone_freq = 16'h1cd5; 80 | 8'h46: midi_note_to_tone_freq = 16'h1e8c; 81 | 8'h47: midi_note_to_tone_freq = 16'h205d; 82 | 8'h48: midi_note_to_tone_freq = 16'h224a; 83 | 8'h49: midi_note_to_tone_freq = 16'h2454; 84 | 8'h4a: midi_note_to_tone_freq = 16'h267d; 85 | 8'h4b: midi_note_to_tone_freq = 16'h28c7; 86 | 8'h4c: midi_note_to_tone_freq = 16'h2b34; 87 | 8'h4d: midi_note_to_tone_freq = 16'h2dc5; 88 | 8'h4e: midi_note_to_tone_freq = 16'h307e; 89 | 8'h4f: midi_note_to_tone_freq = 16'h3360; 90 | 8'h50: midi_note_to_tone_freq = 16'h366f; 91 | 8'h51: midi_note_to_tone_freq = 16'h39ab; 92 | 8'h52: midi_note_to_tone_freq = 16'h3d19; 93 | 8'h53: midi_note_to_tone_freq = 16'h40bb; 94 | 8'h54: midi_note_to_tone_freq = 16'h4495; 95 | 8'h55: midi_note_to_tone_freq = 16'h48a8; 96 | 8'h56: midi_note_to_tone_freq = 16'h4cfb; 97 | 8'h57: midi_note_to_tone_freq = 16'h518e; 98 | 8'h58: midi_note_to_tone_freq = 16'h5668; 99 | 8'h59: midi_note_to_tone_freq = 16'h5b8b; 100 | 8'h5a: midi_note_to_tone_freq = 16'h60fd; 101 | 8'h5b: midi_note_to_tone_freq = 16'h66c1; 102 | 8'h5c: midi_note_to_tone_freq = 16'h6cde; 103 | 8'h5d: midi_note_to_tone_freq = 16'h7357; 104 | 8'h5e: midi_note_to_tone_freq = 16'h7a33; 105 | 8'h5f: midi_note_to_tone_freq = 16'h8177; 106 | 8'h60: midi_note_to_tone_freq = 16'h892a; 107 | 8'h61: midi_note_to_tone_freq = 16'h9151; 108 | 8'h62: midi_note_to_tone_freq = 16'h99f6; 109 | 8'h63: midi_note_to_tone_freq = 16'ha31d; 110 | 8'h64: midi_note_to_tone_freq = 16'hacd0; 111 | 8'h65: midi_note_to_tone_freq = 16'hb717; 112 | 8'h66: midi_note_to_tone_freq = 16'hc1fa; 113 | 8'h67: midi_note_to_tone_freq = 16'hcd83; 114 | 8'h68: midi_note_to_tone_freq = 16'hd9bc; 115 | 8'h69: midi_note_to_tone_freq = 16'he6ae; 116 | 8'h6a: midi_note_to_tone_freq = 16'hf466; 117 | default: midi_note_to_tone_freq = 16'hf466; // 16-bit increment counter doesn't have resolution for further notes 118 | // 8'h6b: midi_note_to_tone_freq = 16'h102ee; 119 | // 8'h6c: midi_note_to_tone_freq = 16'h11254; 120 | // 8'h6d: midi_note_to_tone_freq = 16'h122a3; 121 | // 8'h6e: midi_note_to_tone_freq = 16'h133ec; 122 | // 8'h6f: midi_note_to_tone_freq = 16'h1463b; 123 | // 8'h70: midi_note_to_tone_freq = 16'h159a1; 124 | // 8'h71: midi_note_to_tone_freq = 16'h16e2f; 125 | // 8'h72: midi_note_to_tone_freq = 16'h183f5; 126 | // 8'h73: midi_note_to_tone_freq = 16'h19b07; 127 | // 8'h74: midi_note_to_tone_freq = 16'h1b378; 128 | // 8'h75: midi_note_to_tone_freq = 16'h1cd5c; 129 | // 8'h76: midi_note_to_tone_freq = 16'h1e8cc; 130 | // 8'h77: midi_note_to_tone_freq = 16'h205dc; 131 | // 8'h78: midi_note_to_tone_freq = 16'h224a8; 132 | // 8'h79: midi_note_to_tone_freq = 16'h24547; 133 | // 8'h7a: midi_note_to_tone_freq = 16'h267d8; 134 | // 8'h7b: midi_note_to_tone_freq = 16'h28c77; 135 | // 8'h7c: midi_note_to_tone_freq = 16'h2b343; 136 | // 8'h7d: midi_note_to_tone_freq = 16'h2dc5e; 137 | // 8'h7e: midi_note_to_tone_freq = 16'h307ea; 138 | // 8'h7f: midi_note_to_tone_freq = 16'h3360e; 139 | endcase 140 | 141 | endfunction 142 | 143 | `endif 144 | -------------------------------------------------------------------------------- /examples/midi_voice_control/midi_uart.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MIDI_UART__ 2 | `define __TINY_SYNTH_MIDI_UART__ 3 | 4 | `include "simpleuart.vh" 5 | 6 | /* 7 | * A module which converts raw incoming MIDI byte data into events 8 | * with their parameters, ready for further processing. 9 | * 10 | * ______________ 11 | * byte stream | | 24-bit midi events 12 | * ----------------> | MIDI framer | ---------------------> 13 | * |_____________| 14 | * 15 | * MIDI commands have quite a simple structure. 16 | * 17 | * The first (command) byte can be recognised by the fact that has the MSB set. 18 | * 19 | * Valid commands for our purposes range from 0x80 to 0xEF. 20 | * 21 | * Command Meaning # parameters param 1 param 2 22 | * 0x80 Note-off 2 key velocity 23 | * 0x90 Note-on 2 key velocity 24 | * 0xA0 Aftertouch 2 key touch 25 | * 0xB0 Continuous controller 2 controller # controller value 26 | * 0xC0 Patch change 1 instrument # 27 | * 0xD0 Channel Pressure 1 pressure 28 | * 0xE0 Pitch bend 2 lsb (7 bits) msb (7 bits) 29 | * 0xF0 (non-musical commands) 30 | * 31 | * NOTE: MIDI allows a single "command" byte to be followed by multiple 32 | * sets of parameters - the command byte is remembered. 33 | */ 34 | 35 | module midi_uart( 36 | input [7:0] din, /* input raw byte data from MIDI bus */ 37 | input din_clk, /* input clock (pos edge) */ 38 | input clk, 39 | input serial_rx, 40 | output serial_tx, 41 | 42 | input midi_event_ack, 43 | output reg [7:0] midi_command, /* output: MIDI command byte */ 44 | output reg [6:0] midi_parameter_1, /* output: MIDI parameter #1 */ 45 | output reg [6:0] midi_parameter_2, /* output: MIDI parameter #2 */ 46 | output reg midi_event_valid /* output: MIDI command and parameters contain valid data */ 47 | ); 48 | 49 | wire [7:0] uart_rx_data; 50 | reg uart_read_ack; 51 | wire uart_recv_data_valid; 52 | 53 | wire uart_tx_busy; /* not used */ 54 | 55 | 56 | reg [3:0] state; /* number of MIDI bytes received (including command) */ 57 | reg [21:0] in_flight_midi_message; 58 | reg [2:0] in_flight_expected_param_count; 59 | 60 | simpleuart #(.CLOCK_FREQUENCY(16000000), .BAUD_RATE(31250)) uart( 61 | .clk(clk), .resetn(1'b1), 62 | .ser_rx(serial_rx), 63 | .reg_dat_re(uart_read_ack), 64 | .reg_dat_do(uart_rx_data), 65 | .recv_buf_valid(uart_recv_data_valid), 66 | 67 | /* we never write to the UART */ 68 | .ser_tx(serial_tx), 69 | .reg_dat_we(1'b0), 70 | .reg_dat_di(8'd0), 71 | .tx_busy(uart_tx_busy) 72 | ); 73 | 74 | initial begin 75 | midi_event_valid <= 1'b0; 76 | midi_command <= 8'h00; 77 | midi_parameter_1 <= 7'h00; 78 | midi_parameter_2 <= 7'h00; 79 | 80 | in_flight_expected_param_count <= 2'h0; 81 | end 82 | 83 | function [2:0] expected_midi_parameter_count( 84 | input [3:0] command 85 | ); 86 | begin 87 | case (command) 88 | 4'hc: expected_midi_parameter_count = 3'd1; /* patch change */ 89 | 4'hd: expected_midi_parameter_count = 3'd1; /* channel pressure */ 90 | default: expected_midi_parameter_count = 3'd2; 91 | endcase 92 | end 93 | endfunction 94 | 95 | always @(posedge clk) 96 | begin 97 | if (midi_event_ack) begin 98 | midi_event_valid <= 0; 99 | end 100 | 101 | if (uart_recv_data_valid && !uart_read_ack) begin 102 | // we've just read a new byte from the UART 103 | if (uart_rx_data[7:4] == 4'hf) begin 104 | state <= 4'hf; 105 | end else if (uart_rx_data[7] === 1'b1) begin 106 | // the MSB was set, so this signifies a new MIDI "command" 107 | state <= 1; 108 | in_flight_expected_param_count <= expected_midi_parameter_count(uart_rx_data[7:4]); 109 | in_flight_midi_message <= { uart_rx_data, 14'b0 }; 110 | end else begin 111 | // MSB was not set, so this is a parameter byte 112 | case (state) 113 | 1: begin // waiting for parameter #1 to arrive 114 | if (in_flight_expected_param_count == 1) 115 | begin 116 | // if we only wanted one parameter, then we've got it, so 117 | // clock this out as a valid MIDI event 118 | midi_command <= in_flight_midi_message[21:14]; 119 | midi_parameter_1 <= uart_rx_data; 120 | midi_parameter_2 <= 7'b0; 121 | midi_event_valid <= 1'b1; 122 | state <= 1; 123 | end else begin 124 | // our MIDI command expected more than one parameter, 125 | // so we need to wait for the next to arrive. 126 | in_flight_midi_message <= { in_flight_midi_message[21:14], uart_rx_data[6:0], 7'b0 }; 127 | state <= state + 1; 128 | end 129 | end 130 | 2: begin // waiting for parameter #2 to arrive 131 | if (in_flight_expected_param_count == 2) 132 | begin 133 | // we were expecting two parameters, we've got them, 134 | // so clock them out as a valid MIDI event 135 | midi_command <= in_flight_midi_message[21:14]; 136 | midi_parameter_1 <= in_flight_midi_message[13:7]; 137 | midi_parameter_2 <= uart_rx_data[6:0]; 138 | midi_event_valid <= 1'b1; 139 | end 140 | // we don't support commands with more than 2 parameters, 141 | // so now we transition to a "null" state, until the next 142 | // command arrives. 143 | state <= 1; 144 | end 145 | default: begin 146 | state <= 4'hf; 147 | end 148 | endcase 149 | end 150 | 151 | uart_read_ack <= 1'b1; 152 | end else begin 153 | uart_read_ack <= 1'b0; 154 | end 155 | 156 | end 157 | 158 | endmodule 159 | 160 | `endif 161 | -------------------------------------------------------------------------------- /examples/midi_voice_control/pins.pcf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # TinyFPGA BX constraint file (.pcf) 4 | # 5 | ############################################################################### 6 | # 7 | # Copyright (c) 2018, Luke Valenty 8 | # All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | # 13 | # 1. Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | # 2. Redistributions in binary form must reproduce the above copyright notice, 16 | # this list of conditions and the following disclaimer in the documentation 17 | # and/or other materials provided with the distribution. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | # 30 | # The views and conclusions contained in the software and documentation are those 31 | # of the authors and should not be interpreted as representing official policies, 32 | # either expressed or implied, of the project. 33 | # 34 | ############################################################################### 35 | 36 | #### 37 | # TinyFPGA BX information: https://github.com/tinyfpga/TinyFPGA-BX/ 38 | #### 39 | 40 | # Left side of board 41 | set_io --warn-no-port PIN_1 A2 42 | set_io --warn-no-port PIN_2 A1 43 | #set_io --warn-no-port PIN_3 B1 44 | #set_io --warn-no-port PIN_4 C2 45 | #set_io --warn-no-port PIN_5 C1 46 | #set_io --warn-no-port PIN_6 D2 47 | #set_io --warn-no-port PIN_7 D1 48 | #set_io --warn-no-port PIN_8 E2 49 | #set_io --warn-no-port PIN_9 E1 50 | #set_io --warn-no-port PIN_10 G2 51 | #set_io --warn-no-port PIN_11 H1 52 | #set_io --warn-no-port PIN_12 J1 53 | #set_io --warn-no-port PIN_13 H2 54 | 55 | # Right side of board 56 | #set_io --warn-no-port PIN_14 H9 57 | #set_io --warn-no-port PIN_15 D9 58 | #set_io --warn-no-port PIN_16 D8 59 | #set_io --warn-no-port PIN_17 C9 60 | #set_io --warn-no-port PIN_18 A9 61 | #set_io --warn-no-port PIN_19 B8 62 | #set_io --warn-no-port PIN_20 A8 63 | #set_io --warn-no-port PIN_21 B7 64 | set_io --warn-no-port PIN_22 A7 65 | #set_io --warn-no-port PIN_23 B6 66 | set_io --warn-no-port PIN_24 A6 67 | 68 | # SPI flash interface on bottom of board 69 | #set_io --warn-no-port SPI_SS F7 70 | #set_io --warn-no-port SPI_SCK G7 71 | #set_io --warn-no-port SPI_IO0 G6 72 | #set_io --warn-no-port SPI_IO1 H7 73 | #set_io --warn-no-port SPI_IO2 H4 74 | #set_io --warn-no-port SPI_IO3 J8 75 | 76 | # General purpose pins on bottom of board 77 | #set_io --warn-no-port PIN_25 G1 78 | #set_io --warn-no-port PIN_26 J3 79 | #set_io --warn-no-port PIN_27 J4 80 | #set_io --warn-no-port PIN_28 G9 81 | #set_io --warn-no-port PIN_29 J9 82 | #set_io --warn-no-port PIN_30 E8 83 | #set_io --warn-no-port PIN_31 J2 84 | 85 | # LED 86 | #set_io --warn-no-port LED B3 87 | 88 | # USB 89 | #set_io --warn-no-port USBP B4 90 | #set_io --warn-no-port USBN A4 91 | set_io --warn-no-port USBPU A3 92 | 93 | # 16MHz clock 94 | set_io --warn-no-port CLK B2 # input 95 | -------------------------------------------------------------------------------- /examples/midi_voice_control/q1_table.mem: -------------------------------------------------------------------------------- 1 | // Q1 lookup table (00..7f maps to Q1 value in defined range), 2 | // for use with state variable filter. 3 | // 4 | // generated with gen_q1_table.py with parameters: 5 | // Qmin = 0.7 6 | // Qmax = 4.0 7 | // Ts = 128.0 8 | // 9 | 16db6 10 | 160b9 11 | 1549f 12 | 14953 13 | 13ec1 14 | 134d7 15 | 12b86 16 | 122c1 17 | 11a7b 18 | 112ab 19 | 10b46 20 | 10445 21 | 0fd9f 22 | 0f74e 23 | 0f14b 24 | 0eb92 25 | 0e61c 26 | 0e0e6 27 | 0dbeb 28 | 0d727 29 | 0d297 30 | 0ce37 31 | 0ca05 32 | 0c5fe 33 | 0c21f 34 | 0be66 35 | 0bad1 36 | 0b75e 37 | 0b40b 38 | 0b0d6 39 | 0adbe 40 | 0aac1 41 | 0a7de 42 | 0a513 43 | 0a260 44 | 09fc4 45 | 09d3c 46 | 09ac8 47 | 09868 48 | 0961a 49 | 093de 50 | 091b3 51 | 08f97 52 | 08d8b 53 | 08b8e 54 | 0899f 55 | 087bd 56 | 085e9 57 | 08421 58 | 08264 59 | 080b4 60 | 07f0e 61 | 07d73 62 | 07be2 63 | 07a5c 64 | 078de 65 | 0776a 66 | 075ff 67 | 0749c 68 | 07342 69 | 071ef 70 | 070a4 71 | 06f61 72 | 06e25 73 | 06cef 74 | 06bc1 75 | 06a98 76 | 06977 77 | 0685b 78 | 06745 79 | 06635 80 | 0652a 81 | 06425 82 | 06325 83 | 0622a 84 | 06134 85 | 06043 86 | 05f56 87 | 05e6e 88 | 05d8a 89 | 05cab 90 | 05bd0 91 | 05af8 92 | 05a25 93 | 05955 94 | 05889 95 | 057c1 96 | 056fc 97 | 0563b 98 | 0557d 99 | 054c2 100 | 0540a 101 | 05356 102 | 052a4 103 | 051f6 104 | 0514a 105 | 050a1 106 | 04ffb 107 | 04f57 108 | 04eb6 109 | 04e17 110 | 04d7b 111 | 04ce2 112 | 04c4b 113 | 04bb6 114 | 04b23 115 | 04a93 116 | 04a04 117 | 04978 118 | 048ee 119 | 04866 120 | 047e0 121 | 0475b 122 | 046d9 123 | 04659 124 | 045da 125 | 0455d 126 | 044e2 127 | 04468 128 | 043f0 129 | 0437a 130 | 04306 131 | 04293 132 | 04221 133 | 041b1 134 | 04143 135 | 040d5 136 | 0406a 137 | -------------------------------------------------------------------------------- /examples/midi_voice_control/simpleuart.vh: -------------------------------------------------------------------------------- 1 | /* 2 | * PicoSoC - A simple example SoC using PicoRV32 3 | * 4 | * Copyright (C) 2017 Clifford Wolf 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | */ 19 | 20 | /* 21 | * Some minor modifications have been made to Clifford's code to remove 22 | * some of the SoC integration complexities - I've also commented things 23 | * for my own understanding. 24 | * 25 | */ 26 | `ifndef __PICOSOC_SIMPLEUART__ 27 | `define __PICOSOC_SIMPLEUART__ 28 | 29 | module simpleuart #( 30 | parameter CLOCK_FREQUENCY = 16000000, 31 | parameter BAUD_RATE = 115200 32 | ) ( 33 | input clk, 34 | input resetn, /* active low reset */ 35 | 36 | output ser_tx, /* serial transmit out */ 37 | input ser_rx, /* serial receive in */ 38 | 39 | output recv_buf_valid, 40 | input reg_dat_re, 41 | output [7:0] reg_dat_do, 42 | 43 | input reg_dat_we, 44 | input [7:0] reg_dat_di, 45 | output tx_busy); 46 | 47 | localparam cfg_divider = $rtoi(CLOCK_FREQUENCY / BAUD_RATE); 48 | 49 | /* 50 | * RECEIVE related registers 51 | */ 52 | 53 | /* =============================================================== 54 | * TABLE OF RECEIVE STATES 55 | * =============================================================== 56 | * 57 | * STATE | DESCRIPTION 58 | * ------+------------------------- 59 | * 0 | waiting for start bit 60 | * 1 | start bit detected, waiting for middle of start bit 61 | * 2 | waiting for middle of bit 0 62 | * 3 | waiting for middle of bit 1 63 | * 4 | waiting for middle of bit 2 64 | * 5 | waiting for middle of bit 3 65 | * 6 | waiting for middle of bit 4 66 | * 7 | waiting for middle of bit 5 67 | * 8 | waiting for middle of bit 6 68 | * 9 | waiting for middle of bit 7 69 | * 10 | waiting for middle of stop bit 70 | */ 71 | reg [3:0] recv_state; /* recv_state is essentially a bit counter - indicating which bit the receiver is expecting */ 72 | 73 | reg [31:0] recv_divcnt; /* up-counter, to count cycles for current bit */ 74 | reg [7:0] recv_pattern; /* currently received pattern; clocked in from MSB end */ 75 | reg [7:0] recv_buf_data; /* previously received byte */ 76 | reg recv_buf_valid; /* flag to indicate that the buffered receive byte is valid, and can be clocked out with reg_dat_re */ 77 | 78 | 79 | /* data out line is either data from the buffer (if the data is available and valid), 80 | * or it's 0xff otherwise. 81 | */ 82 | assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0; 83 | assign tx_busy = reg_dat_we && (send_bitcnt || send_dummy); 84 | 85 | /* ===================================================================================================================== */ 86 | 87 | /* 88 | * TRANSMIT related registers 89 | */ 90 | reg [9:0] send_pattern; /* current pattern being clocked out on the serial line */ 91 | reg [3:0] send_bitcnt; /* number of bits remaining to be sent */ 92 | reg [31:0] send_divcnt; /* number of clock cycles counted for the current bit */ 93 | reg send_dummy; /* flag to indicate that the next "slot" is empty */ 94 | 95 | /* reg_dat_wait is asserted when an attempted write is not possible because 96 | * the serial line is busy because it's either currently sending data 97 | * (send_bitcnt is non-zero) or is about to send a dummy slot (send_dummy is 1). 98 | */ 99 | assign tx_busy = reg_dat_we && (send_bitcnt || send_dummy); 100 | 101 | always @(posedge clk) begin 102 | if (!resetn) begin 103 | recv_state <= 0; 104 | recv_divcnt <= 0; 105 | recv_pattern <= 0; 106 | recv_buf_data <= 0; 107 | recv_buf_valid <= 0; 108 | end else begin 109 | recv_divcnt <= recv_divcnt + 1; 110 | 111 | /* if data has been clocked out, reset buf valid flag to 0 */ 112 | if (reg_dat_re) 113 | recv_buf_valid <= 0; 114 | 115 | case (recv_state) 116 | 0: begin 117 | /* if our serial input has been brought low, we're now 118 | * expecting the start bit */ 119 | if (!ser_rx) 120 | recv_state <= 1; 121 | 122 | recv_divcnt <= 0; 123 | end 124 | 1: begin 125 | /* state 1 means we're waiting for the middle of the start bit. */ 126 | if (ser_rx) begin 127 | // we had a false start bit; reset and try again 128 | recv_state <= 0; 129 | end else begin 130 | if (2*recv_divcnt > cfg_divider) begin 131 | /* at this point we've aligned ourselves half-way 132 | * through the start-bit (notice the 2x multiplier above), 133 | * so we reset the counter to zero, and wait for the next 134 | * full-bit cycle(s) so we can sample the middle of the incoming bits. 135 | */ 136 | recv_state <= 2; 137 | recv_divcnt <= 0; 138 | end 139 | end 140 | end 141 | 10: begin 142 | if (recv_divcnt > cfg_divider) begin 143 | /* at this point we're in the middle of receiving the 144 | * stop bit, and rather than doing anything with it, we 145 | * use this as an opportunity to clock out the newly 146 | * received byte by setting the recv_buf_valid flag, 147 | * and then bounce back to state 0. (note: stop-bit is 148 | * logic-level high, so we're ready to look for the next 149 | * high-low transition to signal the next bit). 150 | */ 151 | recv_buf_data <= recv_pattern; 152 | recv_buf_valid <= 1; 153 | recv_state <= 0; 154 | end 155 | end 156 | default: begin 157 | /* states 2-9; clocking in bits 0-7. */ 158 | if (recv_divcnt > cfg_divider) begin 159 | recv_pattern <= {ser_rx, recv_pattern[7:1]}; 160 | recv_state <= recv_state + 1; 161 | recv_divcnt <= 0; 162 | end 163 | end 164 | endcase 165 | end 166 | end 167 | 168 | /* ==================================================================== 169 | * TRANSMIT LOGIC 170 | * ==================================================================== 171 | */ 172 | 173 | // clock out bit zero of send_pattern 174 | assign ser_tx = send_pattern[0]; 175 | 176 | always @(posedge clk) begin 177 | send_divcnt <= send_divcnt + 1; 178 | if (!resetn) begin 179 | /* reset line low, so reset all signals */ 180 | send_pattern <= ~0; 181 | send_bitcnt <= 0; 182 | send_divcnt <= 0; 183 | send_dummy <= 1; 184 | end else begin 185 | if (send_dummy && !send_bitcnt) begin 186 | /* send_dummy requested for next timing slot, 187 | * reset bit count to 15, set send pattern to all 1's, 188 | * clear send_dummy flag */ 189 | send_pattern <= ~0; 190 | send_bitcnt <= 15; 191 | send_divcnt <= 0; 192 | send_dummy <= 0; 193 | end else 194 | if (reg_dat_we && !send_bitcnt) begin 195 | /* write-enable set, and we're not in the process of sending 196 | * any bits, so pull the send_pattern from our data-input port, 197 | * (surrounded by start and stop bits) 198 | */ 199 | send_pattern <= {1'b1, reg_dat_di, 1'b0}; 200 | send_bitcnt <= 10; 201 | send_divcnt <= 0; 202 | end else 203 | if (send_divcnt > cfg_divider && send_bitcnt) begin 204 | /* 205 | * we're ready for the next bit, so rotate send_pattern one 206 | * bit to the right (and fill MSB with a 1). Also 207 | * decrement the number of bits left to send, and 208 | * reset the counter. 209 | */ 210 | send_pattern <= {1'b1, send_pattern[9:1]}; 211 | send_bitcnt <= send_bitcnt - 1; 212 | send_divcnt <= 0; 213 | end 214 | end 215 | end 216 | endmodule 217 | 218 | `endif 219 | -------------------------------------------------------------------------------- /examples/midi_voice_control/top.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny-synth example: playing notes based on MIDI input 3 | */ 4 | 5 | `define __TINY_SYNTH_ROOT_FOLDER "../.." 6 | `include "../../hdl/tiny-synth-all.vh" 7 | `include "midi_player.vh" 8 | 9 | // look in pins.pcf for all the pin names on the TinyFPGA BX board 10 | module top ( 11 | `ifdef blackice 12 | input CLK_100, // 100MHz clock 13 | `else 14 | input CLK, // 16MHz clock 15 | `endif 16 | output USBPU, // USB pull-up resistor 17 | input PIN_22, // serial (MIDI) data in 18 | output PIN_24, // serial (MIDI) data out 19 | output PIN_1, /* audio out left */ 20 | output PIN_2); /* audio out right */ 21 | 22 | `ifdef blackice 23 | wire CLK; 24 | pll pll0 (.clock_in(CLK_100), .clock_out(CLK)); 25 | `endif 26 | 27 | // drive USB pull-up resistor to '0' to disable USB 28 | assign USBPU = 0; 29 | 30 | wire serial_rx; 31 | SB_IO #( 32 | .PIN_TYPE(6'b0000_01), 33 | .PULLUP(1'b0) 34 | ) serial_rx_pin_conf ( 35 | .PACKAGE_PIN(PIN_22), 36 | .D_IN_0(serial_rx) 37 | ); 38 | 39 | localparam SAMPLE_BITS = 12; 40 | 41 | wire signed [SAMPLE_BITS-1:0] final_mix; 42 | midi_player #(.SAMPLE_BITS(SAMPLE_BITS)) midi_player(.clk(CLK), .serial_rx(serial_rx), .serial_tx(PIN_24), .audio_data(final_mix)); 43 | 44 | pdm_dac #(.DATA_BITS(SAMPLE_BITS)) dac_left( 45 | .din(final_mix), 46 | .clk(CLK), // DAC runs at full 16MHz speed. 47 | .dout(PIN_1) 48 | ); 49 | pdm_dac #(.DATA_BITS(SAMPLE_BITS)) dac_right( 50 | .din(final_mix), 51 | .clk(CLK), // DAC runs at full 16MHz speed. 52 | .dout(PIN_2) 53 | ); 54 | 55 | endmodule 56 | -------------------------------------------------------------------------------- /examples/song_player/Makefile: -------------------------------------------------------------------------------- 1 | VERILOG_FILES=top.v ../../blackice//pll.v 2 | PCF_FILE = ../../blackice/blackice.pcf 3 | 4 | include ../../blackice/blackice.mk 5 | 6 | -------------------------------------------------------------------------------- /examples/song_player/README.md: -------------------------------------------------------------------------------- 1 | # Tiny-Synth Example : Playing a song 2 | 3 | ## Description 4 | 5 | This example plays a simple polyphonic song on `PIN_1` using a number of the tiny-synth components. 6 | 7 | ## Important Note 8 | 9 | This demo, probably largely due to a lack of mechanical sympathy on my part, is very close 10 | to using all of the logic resources of the TinyFPGA BX. `arachne-pnr` took almost 30 minutes 11 | on my machine to place and route. 12 | 13 | I've found if I turn off some of the effects like the flanger, things run a lot more quickly. 14 | 15 | An opportunity for future enhancement might be to combine the percussion instruments into a single multplexed voice. 16 | 17 | ## Before you start 18 | 19 | The below circuit (or similar) should be used to filter the output from `PIN_1` before connecting your FPGA to any audio equipment. 20 | 21 | ``` 22 | e.g. 330 ohm eg. 10uF 23 | PIN_1 >---./\/\/\.---o------| |-------> Analog out >-- 24 | | 25 | --- 26 | --- eg. 0.1uF 27 | | 28 | | 29 | --- GND 30 | - 31 | ``` 32 | 33 | ## Principle of operation 34 | 35 | The song player is easiest to understand if we break it into sections. 36 | 37 | ### Instruments 38 | 39 | Instruments present a simple interface: 40 | 41 | ```verilog 42 | module instrument( 43 | input clk, 44 | input wire[16:0] tone_frequency, 45 | input wire gate, 46 | output wire [11:0] audio_data; 47 | ) 48 | ``` 49 | 50 | The two main inputs of interest for the instruments from the point of view of the 51 | song player are `tone_frequency` and `gate`. 52 | `tone_frequency` is used to choose the note that's being played, and `gate` is used 53 | to trigger the instrument. 54 | 55 | For the demo song I've created a number of instruments. 56 | 57 | * A "bass", which is a pulse tone that is passed through an exponentially weighted low-pass filter to smooth off some of the shrillness. 58 | * An "open hi-hat" - a random oscillator with a relatively slow decay envelope 59 | * A "snare" - another random oscillator with a relatively faster decay, at a lower frequency than the hi-hat. 60 | * A "kick drum" - two voices - one is a short, sharp burst of noise; the other is a triangle wave for the "thump". 61 | 62 | The output from all of these instruments is aggregated and collectively passed through a 1.2Hz flanger to give the sound a "richer" timbre. 63 | 64 | ### Bars and rows 65 | 66 | A bar represents a [musical bar](https://en.wikipedia.org/wiki/Bar_(music)), in other words a set of notes that will be played one after the other. 67 | 68 | Unlike a musical bar however, bars in this song player are not polyphonic. We're only able to trigger one instrument in each row. 69 | 70 | An example bar might look like this: 71 | 72 | | Row # | Note | Octave | 73 | | --- | ---: | --- | 74 | |0 | `C` | `2` | 75 | |1 | `C` | `2` | 76 | |2 | `D` | `2` | 77 | |3 | `C` | `2` | 78 | |4 | `D#` | `2` | 79 | |5 | `C` | `2` | 80 | |6 | `F` | `2` | 81 | |7 | `D#` | `2` | 82 | 83 | In the bars above, assuming we're playing in 4/4 time, each time slot represents an eighth note. 84 | 85 | These bars could equally have been written with gaps every second row - ie. each time step representing 1/16th note: 86 | 87 | | Row # | Note | Octave | 88 | | --- | ---: | --- | 89 | |0 | `C` | `2` | 90 | |1 | - | - | 91 | |2 | `C` | `2` | 92 | |3 | - | - | 93 | |4 | `D` | `2` | 94 | |5 | - | - | 95 | |6 | `C` | `2` | 96 | |... | ... | ... | 97 | |... | ... | ... | 98 | |E | `D#` | `2` | 99 | |F | - | - | 100 | 101 | The effect is the same, but each slot now represents an sixteenth note. Assuming that our player plays this bar at twice the rate of our first example, it will sound exactly the same, but if we wanted to we could now insert extra notes in-between the ones that we played before. 102 | 103 | By choosing a bar length that fits with the musical motifs that you're using, you can make composition easier, and also allow the song to compress into a smaller amount of space. 104 | 105 | If you're familiar with "tracker" software from the 90's, this is probably starting to seem quite familiar. 106 | 107 | Bars in the demo-song are stored in the `example_song_bars.rom` file, which also contains a few hints about how the encoding works. Each note is stored as an 8-bit value. The high nibble is the note (`C`=`0x1`,`C#`=`0x2`,...,`B`=`0xC`), and the low nibble is the octave (0..6). A value of `00` means that the note will be skipped. 108 | 109 | ### Ticks 110 | 111 | You may notice a reference to the tick counter in the code if you go digging. 112 | 113 | What I neglected to mention about Bars and Rows above is that each row is actually split into 8 "ticks". 114 | The reason for this is to allow for sub-row processing such as instrument gating and effect processing. 115 | 116 | As it stands now, instruments are gated on for one "tick", and then gated off. A potential future enhancement might be to change the bar structure so that each row could also provide "gate on length". 117 | 118 | ### Patterns 119 | 120 | Patterns combine a number of rows together; each row being assigned to a particular channel. This is where polyphony is introduced. 121 | 122 | #### Example pattern: 123 | 124 | | Channel # | 0 | 1 | 2 | 3 | 125 | | --- | --- | --- | --- | 126 | | *Bar #* | 1 | 2 | 0 | 0 | 127 | 128 | The above pattern can be interpreted as "Play Bar 1 on channel 0, Bar 2 on Channel 1, and Bar 0 on Channels 2 and 3". 129 | 130 | Channels are mapped globally to particular instruments, so in the example above, channel 0 might be a bass instrument, 1 might be assigned to a piano, and 2/3 might be for percussion. 131 | 132 | Patterns in the demo song are stored in the `example_song_patterns.rom` file. 133 | 134 | ### Songs 135 | 136 | You might have guessed already, but a song is really only: 137 | 138 | * Details about the time signature and tempo to use 139 | * A mapping of channels to instruments 140 | * An ordered list of patterns to play 141 | 142 | The ordered list of patterns to play for the demo song is stored in the `example_song_pattern_map.rom` file. 143 | 144 | ### Homework 145 | 146 | Try playing with the code. Change the bar and pattern rom files and see 147 | what effect this has. If you change the song length or number of bars you'll also need to update 148 | the constants in the song_player.vh file. 149 | 150 | Try changing the instrument definitions for each channel in the song_player.vh file. 151 | 152 | Have fun! 153 | -------------------------------------------------------------------------------- /examples/song_player/apio.ini: -------------------------------------------------------------------------------- 1 | [env] 2 | board = TinyFPGA-BX 3 | 4 | -------------------------------------------------------------------------------- /examples/song_player/example_song_bars.rom: -------------------------------------------------------------------------------- 1 | // 8 rows per bar 2 | // 4 bars in this song 3 | // each note = 2 nibbles; { note, octave } 4 | // 5 | // Notes: 6 | // 0 = -- (skip) 7 | // 1 = C 8 | // 2 = C# 9 | // 3 = D 10 | // 4 = D# 11 | // 5 = E 12 | // 6 = F 13 | // 7 = F# 14 | // 8 = G 15 | // 9 = G# 16 | // A = A 17 | // B = A# 18 | // C = B 19 | 20 | // BAR 0 21 | 00 22 | 00 23 | 00 24 | 00 25 | 00 26 | 00 27 | 00 28 | 00 29 | 00 30 | 00 31 | 00 32 | 00 33 | 00 34 | 00 35 | 00 36 | 00 37 | 38 | // BAR 1 (bass line) 39 | 12 40 | 00 41 | 12 42 | 00 43 | 32 44 | 00 45 | 12 46 | 00 47 | 42 48 | 00 49 | 12 50 | 00 51 | 62 52 | 00 53 | 42 54 | 00 55 | 56 | // BAR 2 (bass line) 57 | 62 58 | 00 59 | 62 60 | 00 61 | 82 62 | 00 63 | 62 64 | 00 65 | 92 66 | 00 67 | 62 68 | 00 69 | B2 70 | 00 71 | 92 72 | 00 73 | 74 | // BAR 3 (bass line) 75 | 82 76 | 00 77 | 82 78 | 00 79 | A2 80 | 00 81 | 82 82 | 00 83 | B2 84 | 00 85 | 82 86 | 00 87 | 13 88 | 00 89 | B2 90 | 00 91 | 92 | // bar 4 - kick drum 93 | 13 94 | 00 95 | 00 96 | 00 97 | 00 98 | 00 99 | 00 100 | 00 101 | 13 102 | 00 103 | 00 104 | 00 105 | 00 106 | 00 107 | 00 108 | 00 109 | 110 | // bar 5 - hihat 111 | 00 112 | 00 113 | a6 114 | 00 115 | 00 116 | 00 117 | a6 118 | 00 119 | 00 120 | 00 121 | a6 122 | 00 123 | 00 124 | 00 125 | a6 126 | 00 127 | 128 | // bar 6 - snare 129 | 00 130 | 00 131 | 00 132 | 00 133 | 14 134 | 00 135 | 00 136 | 00 137 | 00 138 | 00 139 | 00 140 | 00 141 | 14 142 | 00 143 | 00 144 | 00 145 | 146 | // bar 7 - snare 2 - with hit on last downbeat 147 | 00 148 | 00 149 | 00 150 | 00 151 | 14 152 | 00 153 | 00 154 | 00 155 | 00 156 | 00 157 | 00 158 | 00 159 | 14 160 | 00 161 | 00 162 | 14 163 | -------------------------------------------------------------------------------- /examples/song_player/example_song_pattern_map.rom: -------------------------------------------------------------------------------- 1 | // Order to play patterns 2 | 00 3 | 00 4 | 00 5 | 00 6 | 01 7 | 01 8 | 00 9 | 00 10 | 02 11 | 01 12 | 00 13 | 00 14 | 03 15 | 06 16 | 03 17 | 06 18 | 04 19 | 07 20 | 03 21 | 06 22 | 05 23 | 07 24 | 03 25 | 06 26 | -------------------------------------------------------------------------------- /examples/song_player/example_song_patterns.rom: -------------------------------------------------------------------------------- 1 | // 4 channels per pattern * N patterns 2 | 3 | // look in the bars ROM to see what each channel will be playing 4 | 5 | // PATTERN 0 6 | 01 00 00 00 7 | 8 | // PATTERN 1 9 | 02 00 00 00 10 | 11 | // PATTERN 2 12 | 03 00 00 00 13 | 14 | // pattern 3 15 | 01 04 05 06 16 | 17 | // pattern 4 18 | 02 04 05 06 19 | 20 | // pattern 5 21 | 03 04 05 06 22 | 23 | // pattern 6 24 | 01 04 05 07 25 | 26 | // pattern 7 27 | 02 04 05 07 28 | 29 | // pattern 8 30 | 03 04 05 07 31 | 32 | // pattern 9 - empty 33 | 00 00 00 00 34 | -------------------------------------------------------------------------------- /examples/song_player/pins.pcf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # TinyFPGA BX constraint file (.pcf) 4 | # 5 | ############################################################################### 6 | # 7 | # Copyright (c) 2018, Luke Valenty 8 | # All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | # 13 | # 1. Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | # 2. Redistributions in binary form must reproduce the above copyright notice, 16 | # this list of conditions and the following disclaimer in the documentation 17 | # and/or other materials provided with the distribution. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | # 30 | # The views and conclusions contained in the software and documentation are those 31 | # of the authors and should not be interpreted as representing official policies, 32 | # either expressed or implied, of the project. 33 | # 34 | ############################################################################### 35 | 36 | #### 37 | # TinyFPGA BX information: https://github.com/tinyfpga/TinyFPGA-BX/ 38 | #### 39 | 40 | # Left side of board 41 | set_io --warn-no-port PIN_1 A2 42 | #set_io --warn-no-port PIN_2 A1 43 | #set_io --warn-no-port PIN_3 B1 44 | #set_io --warn-no-port PIN_4 C2 45 | #set_io --warn-no-port PIN_5 C1 46 | #set_io --warn-no-port PIN_6 D2 47 | #set_io --warn-no-port PIN_7 D1 48 | #set_io --warn-no-port PIN_8 E2 49 | #set_io --warn-no-port PIN_9 E1 50 | #set_io --warn-no-port PIN_10 G2 51 | #set_io --warn-no-port PIN_11 H1 52 | #set_io --warn-no-port PIN_12 J1 53 | #set_io --warn-no-port PIN_13 H2 54 | 55 | # Right side of board 56 | #set_io --warn-no-port PIN_14 H9 57 | #set_io --warn-no-port PIN_15 D9 58 | #set_io --warn-no-port PIN_16 D8 59 | #set_io --warn-no-port PIN_17 C9 60 | #set_io --warn-no-port PIN_18 A9 61 | #set_io --warn-no-port PIN_19 B8 62 | #set_io --warn-no-port PIN_20 A8 63 | #set_io --warn-no-port PIN_21 B7 64 | #set_io --warn-no-port PIN_22 A7 65 | #set_io --warn-no-port PIN_23 B6 66 | #set_io --warn-no-port PIN_24 A6 67 | 68 | # SPI flash interface on bottom of board 69 | #set_io --warn-no-port SPI_SS F7 70 | #set_io --warn-no-port SPI_SCK G7 71 | #set_io --warn-no-port SPI_IO0 G6 72 | #set_io --warn-no-port SPI_IO1 H7 73 | #set_io --warn-no-port SPI_IO2 H4 74 | #set_io --warn-no-port SPI_IO3 J8 75 | 76 | # General purpose pins on bottom of board 77 | #set_io --warn-no-port PIN_25 G1 78 | #set_io --warn-no-port PIN_26 J3 79 | #set_io --warn-no-port PIN_27 J4 80 | #set_io --warn-no-port PIN_28 G9 81 | #set_io --warn-no-port PIN_29 J9 82 | #set_io --warn-no-port PIN_30 E8 83 | #set_io --warn-no-port PIN_31 J2 84 | 85 | # LED 86 | set_io --warn-no-port LED B3 87 | 88 | # USB 89 | #set_io --warn-no-port USBP B4 90 | #set_io --warn-no-port USBN A4 91 | set_io --warn-no-port USBPU A3 92 | 93 | # 16MHz clock 94 | set_io --warn-no-port CLK B2 # input 95 | -------------------------------------------------------------------------------- /examples/song_player/top.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny-synth example: triggering envelope generators from an external pin. 3 | * 4 | * This example will trigger a middle C major chord (C,E,G notes) and play it 5 | * from PIN 1 whenever PIN 13 is brought to ground. 6 | * 7 | * The example makes use of the ADSR envelope generator, which is programmed 8 | * to have a relatively fast attack time, approximately 50% sustain volume, 9 | * and a slow decay. 10 | * 11 | * It also demonstrates the principle of mixing multiple voices into a 12 | * single channel for output. 13 | * 14 | * You will need to make sure that PIN_1 has a low-pass filter and AC coupling 15 | * capacitor on the output as per README.md. 16 | */ 17 | 18 | `define __TINY_SYNTH_ROOT_FOLDER "../.." 19 | `include "song_player.vh" 20 | 21 | // look in pins.pcf for all the pin names on the TinyFPGA BX board 22 | module top ( 23 | `ifdef blackice 24 | input CLK_100, // 100MHz clock 25 | `else 26 | input CLK, // 16MHz clock 27 | `endif 28 | output USBPU, // USB pull-up resistor 29 | output PIN_1); 30 | 31 | `ifdef blackice 32 | wire CLK; 33 | pll pll0 (.clock_in(CLK_100), .clock_out(CLK)); 34 | `endif 35 | 36 | // drive USB pull-up resistor to '0' to disable USB 37 | assign USBPU = 0; 38 | 39 | localparam MAIN_CLK_FREQ = 16000000; 40 | localparam BPM = 120; 41 | 42 | // TICK_HZ is the frequency in Hz that we are 43 | // going to tick through each row in a bar. 44 | 45 | // We want each step in a bar to represent an 1/8th note. 46 | 47 | // At 120bpm, we have 120 quarter-notes per minute. 48 | // This is two quarter-notes per second. 49 | 50 | // This means that we need to send ticks at four ticks 51 | // per second (twice as fast) so that each step represents 52 | // 1/8th note, or 8 ticks per second (four times as fast) 53 | // for 16th notes. 54 | 55 | // The bars in the demo song are arranged as 16th notes, 56 | // so to get the "step" frequency we multiply BPM by 4 57 | // and then divide by 60. 58 | 59 | // Finally, we multiply by eight, because the tick clock 60 | // actually gets divided by 8 in the player so that it 61 | // can perform sub-tick tasks (eg. 62 | // gating/ungating the envelope generator, and in future 63 | // performing effect processing). 64 | 65 | // We want to step through 1 bar in approximately 2 seconds. 66 | localparam TICK_HZ = ((BPM * 4) / 60) * 8; 67 | 68 | // amount we need to divide the main clock by to get our tick clock 69 | localparam TICK_DIVISOR = $rtoi(MAIN_CLK_FREQ / TICK_HZ); 70 | 71 | wire tick_clock; 72 | clock_divider #(.DIVISOR(TICK_DIVISOR)) tick_divider(.cin(CLK), .cout(tick_clock)); 73 | 74 | wire ONE_MHZ_CLK; 75 | clock_divider #(.DIVISOR(16)) mhz_clk_divider(.cin(CLK), .cout(ONE_MHZ_CLK)); 76 | 77 | localparam SAMPLE_BITS = 12; 78 | 79 | // divide main clock down to 44100Hz for sample output (note this clock will have 80 | // a bit of jitter because 44.1kHz doesn't go evenly into 16MHz). 81 | wire SAMPLE_CLK; 82 | clock_divider #( 83 | .DIVISOR((16000000/44100)) 84 | ) sample_clk_divider(.cin(CLK), .cout(SAMPLE_CLK)); 85 | 86 | signed wire[SAMPLE_BITS-1:0] final_mix; 87 | song_player #(.DATA_BITS(SAMPLE_BITS)) player(.main_clk(ONE_MHZ_CLK), .sample_clk(SAMPLE_CLK), .tick_clock(tick_clock), .audio_out(final_mix)); 88 | 89 | pdm_dac #(.DATA_BITS(SAMPLE_BITS)) dac1( 90 | .din(final_mix), 91 | .clk(CLK), // DAC runs at full 16MHz speed. 92 | .dout(PIN_1) 93 | ); 94 | 95 | endmodule 96 | -------------------------------------------------------------------------------- /examples/triggered_adsr_voice/README.md: -------------------------------------------------------------------------------- 1 | # Tiny-Synth Example : External gate control of C Major chord 2 | 3 | ## Description 4 | 5 | This example configures three triangle-wave tone generators, one for each of the notes C, E, and G. Together these notes form a C-Major chord. 6 | 7 | The chord is triggered whenever `PIN_13` is brought to ground. 8 | 9 | The resulting audio signal is routed to `PIN_1`. 10 | 11 | The example makes use of tiny-synth's `envelope_generator` module, which is programmed to have a relatively fast attack time, approximately 50% sustain volume, and a slow decay. 12 | 13 | The code also demonstrates the principle of mixing three voices into a single channel using the `two_into_one_mixer` module for output, and using the `pdm_dac` module for generating an "analog" signal. 14 | 15 | You will need to make sure that `PIN_1` has a low-pass filter and AC coupling capacitor on the output as per README.md, and you will also need to have a way of momentarily bringing `PIN_13` to ground (eg. a switch). 16 | 17 | ## Before you start 18 | 19 | The below circuit can be used to filter the output from `PIN_1`. 20 | 21 | ``` 22 | e.g. 330 ohm eg. 10uF 23 | PIN_1 >---./\/\/\.---o------| |-------> Analog out >-- 24 | | 25 | --- 26 | --- eg. 0.1uF 27 | | 28 | | 29 | --- GND 30 | - 31 | ``` 32 | 33 | You will also need to wire a switch between PIN 13 and ground. 34 | 35 | This switch is used to "gate" the waveform. 36 | 37 | ``` 38 | 39 | PIN 13 >----. 40 | | 41 | o 42 | \ <-- SW1: trigger waveform 43 | \ 44 | o 45 | | 46 | __|__ 47 | --- 48 | - 49 | ``` 50 | -------------------------------------------------------------------------------- /examples/triggered_adsr_voice/apio.ini: -------------------------------------------------------------------------------- 1 | [env] 2 | board = TinyFPGA-BX 3 | 4 | -------------------------------------------------------------------------------- /examples/triggered_adsr_voice/pins.pcf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # TinyFPGA BX constraint file (.pcf) 4 | # 5 | ############################################################################### 6 | # 7 | # Copyright (c) 2018, Luke Valenty 8 | # All rights reserved. 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | # 13 | # 1. Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | # 2. Redistributions in binary form must reproduce the above copyright notice, 16 | # this list of conditions and the following disclaimer in the documentation 17 | # and/or other materials provided with the distribution. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | # 30 | # The views and conclusions contained in the software and documentation are those 31 | # of the authors and should not be interpreted as representing official policies, 32 | # either expressed or implied, of the project. 33 | # 34 | ############################################################################### 35 | 36 | #### 37 | # TinyFPGA BX information: https://github.com/tinyfpga/TinyFPGA-BX/ 38 | #### 39 | 40 | # Left side of board 41 | set_io --warn-no-port PIN_1 A2 42 | set_io --warn-no-port PIN_2 A1 43 | #set_io --warn-no-port PIN_3 B1 44 | #set_io --warn-no-port PIN_4 C2 45 | #set_io --warn-no-port PIN_5 C1 46 | #set_io --warn-no-port PIN_6 D2 47 | #set_io --warn-no-port PIN_7 D1 48 | #set_io --warn-no-port PIN_8 E2 49 | #set_io --warn-no-port PIN_9 E1 50 | #set_io --warn-no-port PIN_10 G2 51 | #set_io --warn-no-port PIN_11 H1 52 | #set_io --warn-no-port PIN_12 J1 53 | set_io --warn-no-port PIN_13 H2 54 | 55 | # Right side of board 56 | #set_io --warn-no-port PIN_14 H9 57 | #set_io --warn-no-port PIN_15 D9 58 | #set_io --warn-no-port PIN_16 D8 59 | #set_io --warn-no-port PIN_17 C9 60 | #set_io --warn-no-port PIN_18 A9 61 | #set_io --warn-no-port PIN_19 B8 62 | #set_io --warn-no-port PIN_20 A8 63 | #set_io --warn-no-port PIN_21 B7 64 | #set_io --warn-no-port PIN_22 A7 65 | #set_io --warn-no-port PIN_23 B6 66 | #set_io --warn-no-port PIN_24 A6 67 | 68 | # SPI flash interface on bottom of board 69 | #set_io --warn-no-port SPI_SS F7 70 | #set_io --warn-no-port SPI_SCK G7 71 | #set_io --warn-no-port SPI_IO0 G6 72 | #set_io --warn-no-port SPI_IO1 H7 73 | #set_io --warn-no-port SPI_IO2 H4 74 | #set_io --warn-no-port SPI_IO3 J8 75 | 76 | # General purpose pins on bottom of board 77 | #set_io --warn-no-port PIN_25 G1 78 | #set_io --warn-no-port PIN_26 J3 79 | #set_io --warn-no-port PIN_27 J4 80 | #set_io --warn-no-port PIN_28 G9 81 | #set_io --warn-no-port PIN_29 J9 82 | #set_io --warn-no-port PIN_30 E8 83 | #set_io --warn-no-port PIN_31 J2 84 | 85 | # LED 86 | set_io --warn-no-port LED B3 87 | 88 | # USB 89 | #set_io --warn-no-port USBP B4 90 | #set_io --warn-no-port USBN A4 91 | set_io --warn-no-port USBPU A3 92 | 93 | # 16MHz clock 94 | set_io --warn-no-port CLK B2 # input 95 | -------------------------------------------------------------------------------- /examples/triggered_adsr_voice/top.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny-synth example: triggering envelope generators from an external pin. 3 | * 4 | * This example will trigger a middle C major chord (C,E,G notes) and play it 5 | * from PIN 1 whenever PIN 13 is brought to ground. 6 | * 7 | * The example makes use of the ADSR envelope generator, which is programmed 8 | * to have a relatively fast attack time, approximately 50% sustain volume, 9 | * and a slow decay. 10 | * 11 | * It also demonstrates the principle of mixing multiple voices into a 12 | * single channel for output. 13 | * 14 | * You will need to make sure that PIN_1 has a low-pass filter and AC coupling 15 | * capacitor on the output as per README.md. 16 | */ 17 | 18 | `define __TINY_SYNTH_ROOT_FOLDER "../.." 19 | `include "../../hdl/tiny-synth-all.vh" 20 | 21 | // look in pins.pcf for all the pin names on the TinyFPGA BX board 22 | module top ( 23 | input CLK, // 16MHz clock 24 | input PIN_13, // gate 25 | output USBPU, // USB pull-up resistor 26 | output PIN_1); 27 | 28 | // drive USB pull-up resistor to '0' to disable USB 29 | assign USBPU = 0; 30 | 31 | signed wire [11:0] voice_data_c; 32 | signed wire [11:0] voice_data_e; 33 | signed wire [11:0] voice_data_g; 34 | 35 | reg trigger_in; 36 | SB_IO #( 37 | .PIN_TYPE(6'b0000_01), 38 | .PULLUP(1'b1) 39 | ) gate_trigger_io_conf ( 40 | .PACKAGE_PIN(PIN_13), 41 | .D_IN_0(trigger_in) 42 | ); 43 | 44 | wire ONE_MHZ_CLK; /* 1MHz clock for tone generator */ 45 | clock_divider #(.DIVISOR(16)) mhzclkgen (.cin(CLK), .cout(ONE_MHZ_CLK)); 46 | 47 | wire SAMPLE_CLK; 48 | clock_divider #( 49 | .DIVISOR((16000000/44100)) 50 | ) sample_clk_divider(.cin(CLK), .cout(SAMPLE_CLK)); 51 | 52 | // tone_freq is calculated by (16777216 * freq) / 1000000 53 | // so, for 261.63Hz (Middle C), tone_freq needs to be 4389. 54 | voice voice_c( 55 | .main_clk(ONE_MHZ_CLK), .sample_clk(SAMPLE_CLK), .tone_freq(16'd4389) /* C4, 261.63Hz */, .rst(1'b0), .test(1'b0), 56 | .en_ringmod(1'b0), .ringmod_source(1'b0), 57 | .en_sync(1'b0), .sync_source(1'b0), 58 | .waveform_enable(4'b0001), .pulse_width(12'd2047), 59 | .dout(voice_data_c), 60 | .attack(4'b0010), .decay(4'b0010), .sustain(4'b1000), .rel(4'b1100), 61 | .gate(!trigger_in) 62 | ); 63 | 64 | voice voice_e( 65 | .main_clk(ONE_MHZ_CLK), .sample_clk(SAMPLE_CLK), .tone_freq(16'd5530) /* E4, 329.63Hz */, .rst(1'b0), .test(1'b0), 66 | .en_ringmod(1'b0), .ringmod_source(1'b0), 67 | .en_sync(1'b0), .sync_source(1'b0), 68 | .waveform_enable(4'b0001), .pulse_width(12'd2047), 69 | .dout(voice_data_e), 70 | .attack(4'b0010), .decay(4'b0010), .sustain(4'b1000), .rel(4'b1100), 71 | .gate(!trigger_in) 72 | ); 73 | 74 | voice voice_g( 75 | .main_clk(ONE_MHZ_CLK), .sample_clk(SAMPLE_CLK), .tone_freq(16'd6577) /* G4, 392.00Hz */, .rst(1'b0), .test(1'b0), 76 | .en_ringmod(1'b0), .ringmod_source(1'b0), 77 | .en_sync(1'b0), .sync_source(1'b0), 78 | .waveform_enable(4'b0001), .pulse_width(12'd2047), 79 | .dout(voice_data_g), 80 | .attack(4'b0010), .decay(4'b0010), .sustain(4'b1000), .rel(4'b1100), 81 | .gate(!trigger_in) 82 | ); 83 | 84 | signed wire [11:0] intermediate_mix; 85 | signed wire [11:0] final_mix; 86 | 87 | two_into_one_mixer intermediate_mixer(.a(voice_data_c), .b(voice_data_e), .dout(intermediate_mix)); 88 | two_into_one_mixer final_mixer(.a(intermediate_mix), .b(voice_data_g), .dout(final_mix)); 89 | 90 | pdm_dac #(.DATA_BITS(12)) dac1( 91 | .din(final_mix), 92 | .clk(CLK), 93 | .dout(PIN_1) 94 | ); 95 | 96 | endmodule 97 | -------------------------------------------------------------------------------- /hdl/amplitude_modulator.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_AMPLITUDE_MODULATOR__ 2 | `define __TINY_SYNTH_AMPLITUDE_MODULATOR__ 3 | /* ===================== 4 | * amplitude modulator 5 | * ===================== 6 | * 7 | * An amplitude modulator; used, for example, to adjust the output volume 8 | * of the tone generator based on the output of the ADSR envelope generator. 9 | * 10 | * Principle of operation: 11 | * 12 | * Converts data-in (din) to a signed value (-128..127 instead of 0..255), 13 | * scales it according to the amplitude input, and then converts the result back 14 | * to unsigned again for output. 15 | * 16 | */ 17 | module amplitude_modulator #( 18 | parameter DATA_BITS = 12, 19 | parameter AMPLITUDE_BITS = 8 20 | ) 21 | ( 22 | input signed [DATA_BITS-1:0] din, 23 | input [AMPLITUDE_BITS-1:0] amplitude, 24 | input clk, 25 | output wire signed [DATA_BITS-1:0] dout 26 | ); 27 | 28 | // cajole amplitude into a signed value so that verilog 29 | // uses signed arithmetic in the multiply below 30 | wire signed [AMPLITUDE_BITS:0] amp_signed; 31 | assign amp_signed = { 1'b0, amplitude[AMPLITUDE_BITS-1:0] }; // amplitude with extra MSB (0) 32 | 33 | reg signed [DATA_BITS+AMPLITUDE_BITS-1:0] scaled_din; // intermediate value with extended precision 34 | 35 | always @(posedge clk) begin 36 | scaled_din <= (din * amp_signed); 37 | end 38 | 39 | assign dout = scaled_din[DATA_BITS+AMPLITUDE_BITS-1 -: DATA_BITS]; 40 | 41 | endmodule 42 | 43 | `endif 44 | -------------------------------------------------------------------------------- /hdl/clock_divider.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_CLOCK_DIVIDER__ 2 | `define __TINY_SYNTH_CLOCK_DIVIDER__ 3 | 4 | module clock_divider #( 5 | parameter DIVISOR = 2 6 | ) 7 | ( 8 | input wire cin, 9 | output wire cout 10 | ); 11 | 12 | localparam FULL_SCALE = 2 ** 28; 13 | localparam [27:0] INCREMENT = $rtoi(FULL_SCALE / DIVISOR); 14 | 15 | reg [27:0] counter; 16 | 17 | initial begin 18 | counter = 0; 19 | end 20 | 21 | always @(posedge cin) 22 | begin 23 | counter = counter + INCREMENT; 24 | end 25 | 26 | assign cout = counter[27]; 27 | 28 | endmodule 29 | 30 | `endif 31 | -------------------------------------------------------------------------------- /hdl/eight_bit_exponential_decay_lookup.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_EXP_LOOKUP_TABLE__ 2 | `define __TINY_SYNTH_EXP_LOOKUP_TABLE__ 3 | 4 | /* 5 | * map from 8-bit -> 8-bit value for exponential falloff of decay and release 6 | * in the envelope generator. 7 | */ 8 | 9 | `ifndef __TINY_SYNTH_ROOT_FOLDER 10 | `define __TINY_SYNTH_ROOT_FOLDER ".." 11 | `endif 12 | 13 | module eight_bit_exponential_decay_lookup ( 14 | input wire [7:0] din, 15 | output wire [7:0] dout 16 | ); 17 | 18 | reg [0:7] exp_lookup [0:255]; 19 | initial $readmemh({`__TINY_SYNTH_ROOT_FOLDER , "/data/exp_lookup_table.rom"}, exp_lookup); 20 | 21 | assign dout = exp_lookup[din]; 22 | 23 | endmodule 24 | 25 | `endif 26 | -------------------------------------------------------------------------------- /hdl/filter_ewma.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_FILTER_EWMA__ 2 | `define __TINY_SYNTH_FILTER_EWMA__ 3 | /* ======================================================== 4 | * Exponentially weighted moving average (low-pass) filter 5 | * ======================================================== 6 | * 7 | * This module implements a simple single-multiply exponentially 8 | * weighted moving average (EWMA) low-pass filter. 9 | * 10 | * Principle of operation: 11 | * 12 | * This filter is well described by Rick Lyons in his article 13 | * here - refer to diagram 1b: 14 | * 15 | * https://www.dsprelated.com/showarticle/182.php 16 | * 17 | * This filter has a single parameter, alpha, that can be used to 18 | * control the cut-off frequency. 19 | * 20 | * Use the following formula to calculate alpha based on desired -3dB cut-off: 21 | * 22 | * Fs = sample rate 23 | * Fc = cutoff frequency 24 | * b = cos(2*pi*Fc / Fs); 25 | * 26 | * alpha = 255 * b - 1 + sqrt(b^2 - 4*b + 3); 27 | */ 28 | 29 | /* Example values for alpha based on Fc/Fs: 30 | * 31 | * =================== 32 | * Fc(-3dB)/Fs alpha 33 | * ------------------- 34 | * 0.01, 16 35 | * 0.02, 30 36 | * 0.03, 44 37 | * 0.04, 56 38 | * 0.05, 68 39 | * 0.06, 79 40 | * 0.07, 90 41 | * 0.08, 99 42 | * 0.09, 108 43 | * 0.09, 116 44 | * 0.10, 124 45 | * 0.11, 131 46 | * 0.12, 137 47 | * 0.13, 144 48 | * 0.15, 149 49 | * 0.16, 154 50 | * 0.17, 159 51 | * 0.18, 164 52 | * 0.19, 168 53 | * 0.20, 172 54 | * 0.21, 175 55 | * 0.22, 178 56 | * 0.23, 181 57 | * 0.24, 184 58 | * 0.25, 187 59 | * 0.26, 189 60 | * 0.27, 191 61 | * 0.28, 193 62 | * 0.29, 195 63 | * 0.30, 197 64 | * 0.31, 199 65 | * 0.32, 200 66 | */ 67 | 68 | module filter_ewma #( 69 | parameter DATA_BITS = 12 70 | ) ( 71 | input clk, 72 | input wire signed [8:0] s_alpha, 73 | input wire signed [DATA_BITS-1:0] din, /* unfiltered data in */ 74 | output reg signed [DATA_BITS-1:0] dout /* filtered data out */ 75 | ); 76 | 77 | localparam HALF_SCALE = (2**(DATA_BITS-1)); 78 | 79 | initial 80 | begin 81 | dout = 0; 82 | s_adder1_out = 0; 83 | end 84 | 85 | reg signed [DATA_BITS:0] s_adder1_out; 86 | reg signed [(DATA_BITS+8+1):0] sw_raw_mul_output; 87 | reg signed [DATA_BITS:0] s_mul_out; 88 | reg signed [DATA_BITS:0] tmp_dout; 89 | 90 | always @(posedge clk) 91 | begin 92 | // copy previous dout to delay line 93 | s_adder1_out = din - dout; 94 | sw_raw_mul_output = (s_adder1_out * s_alpha) >>> 8; // divide by 256 (amplitude) 95 | s_mul_out = sw_raw_mul_output[DATA_BITS:0]; 96 | tmp_dout = (s_mul_out + dout); 97 | dout = tmp_dout[DATA_BITS-1:0]; 98 | end 99 | 100 | endmodule 101 | 102 | `endif 103 | -------------------------------------------------------------------------------- /hdl/filter_svf.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_SVF__ 2 | `define __TINY_SYNTH_SVF__ 3 | 4 | /* 5 | * State variable filter: 6 | * Ref: Musical applications of microprocessors - Chamberlain: pp489+ 7 | * 8 | * This filter provides high-pass, low-pass, band-pass and notch-pass outputs. 9 | * 10 | * Tuning parameters are F (frequency), and Q1 of the filter. 11 | * 12 | * The relation between F, the cut-off frequency Fc, 13 | * and the sampling rate, Fs, is approximated by the formula: 14 | * 15 | * F = 2π*Fc/Fs 16 | * 17 | * F is a 1.17 fixed-point value, and at a sample rate of 250kHz, 18 | * F ranges from approximately 0.00050 (10Hz) -> ~0.55 (22kHz). 19 | * 20 | * Q1 controls the Q (resonance) of the filter. Q1 is equivalent to 1/Q. 21 | * Q1 ranges from 2 (corresponding to a Q value of 0.5) down to 0 (Q = infinity) 22 | */ 23 | 24 | module filter_svf #( 25 | parameter SAMPLE_BITS = 12 26 | )( 27 | input clk, 28 | input signed [SAMPLE_BITS-1:0] in, 29 | output signed [SAMPLE_BITS-1:0] out_highpass, 30 | output signed [SAMPLE_BITS-1:0] out_lowpass, 31 | output signed [SAMPLE_BITS-1:0] out_bandpass, 32 | output signed [SAMPLE_BITS-1:0] out_notch, 33 | input signed [17:0] F, /* F1: frequency control; fixed point 1.17 ; F = 2sin(π*Fc/Fs). At a sample rate of 250kHz, F ranges from 0.00050 (10Hz) -> ~0.55 (22kHz) */ 34 | input signed [17:0] Q1 /* Q1: Q control; fixed point 2.16 ; Q1 = 1/Q Q1 ranges from 2 (Q=0.5) to 0 (Q = infinity). */ 35 | ); 36 | 37 | 38 | reg signed[SAMPLE_BITS+2:0] highpass; 39 | reg signed[SAMPLE_BITS+2:0] lowpass; 40 | reg signed[SAMPLE_BITS+2:0] bandpass; 41 | reg signed[SAMPLE_BITS+2:0] notch; 42 | reg signed[SAMPLE_BITS+2:0] in_sign_extended; 43 | 44 | localparam signed [SAMPLE_BITS+2:0] MAX = (2**(SAMPLE_BITS-1))-1; 45 | localparam signed [SAMPLE_BITS+2:0] MIN = -(2**(SAMPLE_BITS-1)); 46 | 47 | `define CLAMP(x) ((x>MAX)?MAX:((x>> 16; 71 | F_scaled_delayed_bandpass = (bandpass * F) >>> 17; 72 | lowpass = lowpass + F_scaled_delayed_bandpass[SAMPLE_BITS+2:0]; 73 | highpass = in_sign_extended - lowpass - Q1_scaled_delayed_bandpass[SAMPLE_BITS+2:0]; 74 | F_scaled_highpass = (highpass * F) >>> 17; 75 | bandpass = F_scaled_highpass[SAMPLE_BITS+2:0] + bandpass; 76 | notch = highpass + lowpass; 77 | end 78 | 79 | endmodule 80 | 81 | `endif 82 | -------------------------------------------------------------------------------- /hdl/filter_svf_pipelined.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_SVF_PIPELINED__ 2 | `define __TINY_SYNTH_SVF_PIPELINED__ 3 | 4 | /* 5 | * State variable filter: 6 | * Ref: Musical applications of microprocessors - Chamberlain: pp489+ 7 | * 8 | * NOTE: this filter should be functionally equivalent to filter_svh, except 9 | * that it is pipelined, and requires a higher frequency clock as well 10 | * as the sample clock. 11 | * 12 | * This implementation uses only a single 18*18 multiplier, rather than 13 | * 3, so should save a lot in terms of gate count. 14 | * 15 | * The downside is that the filter takes 4 clock cycles for each sample. 16 | * 17 | * This filter provides high-pass, low-pass, band-pass and notch-pass outputs. 18 | * 19 | * Tuning parameters are F (frequency), and Q1 of the filter. 20 | * 21 | * The relation between F, the cut-off frequency Fc, 22 | * and the sampling rate, Fs, is approximated by the formula: 23 | * 24 | * F = 2π*Fc/Fs 25 | * 26 | * F is a 1.17 fixed-point value, and at a sample rate of 250kHz, 27 | * F ranges from approximately 0.00050 (10Hz) -> ~0.55 (22kHz). 28 | * 29 | * Q1 controls the Q (resonance) of the filter. Q1 is equivalent to 1/Q. 30 | * Q1 ranges from 2 (corresponding to a Q value of 0.5) down to 0 (Q = infinity) 31 | */ 32 | 33 | /* multiplier module */ 34 | module smul_18x18 ( 35 | input signed [17:0] a, 36 | input signed [17:0] b, 37 | output reg signed [35:0] o 38 | ); 39 | always @(*) begin 40 | o = a * b; 41 | end 42 | endmodule 43 | 44 | module filter_svf_pipelined #( 45 | parameter SAMPLE_BITS = 12 46 | )( 47 | input clk, 48 | input sample_clk, 49 | input signed [SAMPLE_BITS-1:0] in, 50 | output reg signed [SAMPLE_BITS-1:0] out_highpass, 51 | output reg signed [SAMPLE_BITS-1:0] out_lowpass, 52 | output reg signed [SAMPLE_BITS-1:0] out_bandpass, 53 | output reg signed [SAMPLE_BITS-1:0] out_notch, 54 | input signed [17:0] F, /* F1: frequency control; fixed point 1.17 ; F = 2sin(π*Fc/Fs). At a sample rate of 250kHz, F ranges from 0.00050 (10Hz) -> ~0.55 (22kHz) */ 55 | input signed [17:0] Q1 /* Q1: Q control; fixed point 2.16 ; Q1 = 1/Q Q1 ranges from 2 (Q=0.5) to 0 (Q = infinity). */ 56 | ); 57 | 58 | 59 | reg signed[SAMPLE_BITS+2:0] highpass; 60 | reg signed[SAMPLE_BITS+2:0] lowpass; 61 | reg signed[SAMPLE_BITS+2:0] bandpass; 62 | reg signed[SAMPLE_BITS+2:0] notch; 63 | reg signed[SAMPLE_BITS+2:0] in_sign_extended; 64 | 65 | localparam signed [SAMPLE_BITS+2:0] MAX = (2**(SAMPLE_BITS-1))-1; 66 | localparam signed [SAMPLE_BITS+2:0] MIN = -(2**(SAMPLE_BITS-1)); 67 | 68 | `define CLAMP(x) ((x>MAX)?MAX:((x>> 16; 117 | Q1_scaled_delayed_bandpass <= (mul_out >> 16); 118 | mul_b <= F; 119 | state <= 3'd1; 120 | end 121 | 3'd1: begin 122 | // F_scaled_delayed_bandpass = (bandpass * F) >>> 17; 123 | F_scaled_delayed_bandpass = (mul_out >> 17); 124 | lowpass = lowpass + F_scaled_delayed_bandpass[SAMPLE_BITS+2:0]; 125 | highpass = in_sign_extended - lowpass - Q1_scaled_delayed_bandpass[SAMPLE_BITS+2:0]; 126 | mul_a <= highpass; 127 | state <= 3'd2; 128 | end 129 | 3'd2: begin 130 | F_scaled_highpass = mul_out >> 17; 131 | bandpass <= F_scaled_highpass[SAMPLE_BITS+2:0] + bandpass; 132 | notch <= highpass + lowpass; 133 | state <= 3'd3; 134 | end 135 | endcase 136 | 137 | // in_sign_extended = { in[SAMPLE_BITS-1], in[SAMPLE_BITS-1], in[SAMPLE_BITS-1], in}; /* sign-extend the input value to a wider precision */ 138 | // Q1_scaled_delayed_bandpass = (bandpass * Q1) >>> 16; 139 | // F_scaled_delayed_bandpass = (bandpass * F) >>> 17; 140 | // lowpass = lowpass + F_scaled_delayed_bandpass[SAMPLE_BITS+2:0]; 141 | // highpass = in_sign_extended - lowpass - Q1_scaled_delayed_bandpass[SAMPLE_BITS+2:0]; 142 | // F_scaled_highpass = (highpass * F) >>> 17; 143 | // bandpass = F_scaled_highpass[SAMPLE_BITS+2:0] + bandpass; 144 | // notch = highpass + lowpass; 145 | end 146 | 147 | endmodule 148 | 149 | `endif 150 | -------------------------------------------------------------------------------- /hdl/flanger.vh: -------------------------------------------------------------------------------- 1 | /* ======================================================== 2 | * Flanger 3 | * ======================================================== 4 | * 5 | * This module implements a simple flanger effect. 6 | * 7 | * Principle of operation: 8 | * 9 | * A flanger works by mixing the input with a delayed version 10 | * of itself - where the delay length is, itself, modulated. 11 | * 12 | * In this instance, we use an ICE40 SPRAM block as storage 13 | * for the delay line, and use a triangle-wave modulator for 14 | * the length of the line. 15 | * 16 | * The line can be any length up to 256 entries - the following table 17 | * shows the different lengths and their equivalent times in milliseconds 18 | * at a 44.1kHz sample rate: 19 | * 20 | * 256 -> 5.8ms 21 | * 128 -> 2.9ms 22 | * 64 -> 1.45ms 23 | * 32 -> 0.7ms 24 | * ... 25 | * 26 | */ 27 | 28 | module flanger #( 29 | parameter DELAY_BUFFER_LENGTH_BITS = 8, /* = 8-bits = 256 long = 5.6 milliseconds @ 44.1kHz */ 30 | parameter SAMPLE_BITS = 12, 31 | parameter SAMPLE_RATE = 44100, /* Hz */ 32 | parameter FLANGE_RATE = 1.4, /* Hz */ 33 | parameter ACCUMULATOR_BITS = 21 34 | ) 35 | ( 36 | input wire sample_clk, 37 | input wire signed [SAMPLE_BITS-1:0] din, 38 | output reg signed [SAMPLE_BITS-1:0] dout 39 | ); 40 | 41 | localparam DELAY_BUFFER_LENGTH = 2**DELAY_BUFFER_LENGTH_BITS; 42 | localparam DELAY_BUFFER_MAX = DELAY_BUFFER_LENGTH-1; 43 | 44 | localparam SAMPLE_HIGH_BIT = 2**(SAMPLE_BITS-1); 45 | 46 | // reg [SAMPLE_BITS-1:0] delay_buffer [0:DELAY_BUFFER_LENGTH-1]; 47 | 48 | // top bits of accumulator give us our current tap point in the delay buffer 49 | reg [ACCUMULATOR_BITS-1:0] accumulator; 50 | reg [7:0] delay_buffer_write_address; 51 | 52 | reg signed [SAMPLE_BITS-1:0] delay_tap_output; 53 | reg[7:0] delay_buffer_read_address; 54 | wire [DELAY_BUFFER_LENGTH_BITS-1:0] delay_buffer_tap_index; /* output of triangle oscillator */ 55 | assign delay_buffer_tap_index = (accumulator[ACCUMULATOR_BITS-1]==1'b1) 56 | ? ~accumulator[(ACCUMULATOR_BITS-2) -: DELAY_BUFFER_LENGTH_BITS] 57 | : accumulator[(ACCUMULATOR_BITS-2) -: DELAY_BUFFER_LENGTH_BITS]; 58 | 59 | assign delay_buffer_read_address = ((delay_buffer_write_address-delay_buffer_tap_index)&DELAY_BUFFER_MAX); 60 | 61 | // WRITE_MODE = 0 and READ_MODE = 0 configures this as a 256 x 16-bit RAM 62 | SB_RAM40_4K #(.WRITE_MODE(0), .READ_MODE(0)) delay_buffer ( 63 | // read side of buffer; serial port to FPGA 64 | .RDATA(delay_tap_output), 65 | .RADDR(delay_buffer_read_address), 66 | .RCLK(sample_clk), 67 | .RE(1'b1), 68 | 69 | // write side of buffer; FPGA to serial port 70 | .WADDR(delay_buffer_write_address), 71 | .WCLK(sample_clk), 72 | .WDATA(din), 73 | .WE(1'b1) 74 | ); 75 | 76 | initial 77 | begin 78 | accumulator = 0; 79 | delay_buffer_write_address = 0; 80 | end 81 | 82 | localparam ACCUMULATOR_MAX_SCALE = 2**ACCUMULATOR_BITS; 83 | localparam ACCUMULATOR_PHASE_INCREMENT = $rtoi((ACCUMULATOR_MAX_SCALE * FLANGE_RATE) / 44100); 84 | 85 | reg signed [SAMPLE_BITS:0] tmp; 86 | 87 | always @(posedge sample_clk) 88 | begin 89 | 90 | // write current sample into delay buffer 91 | delay_buffer_write_address <= delay_buffer_write_address + 1; 92 | accumulator <= accumulator + ACCUMULATOR_PHASE_INCREMENT; 93 | 94 | tmp = din + delay_tap_output; 95 | dout <= tmp >>> 1; 96 | end 97 | 98 | 99 | endmodule 100 | -------------------------------------------------------------------------------- /hdl/multi_channel_mixer.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_MULTI_CHANNEL_MIXER__ 2 | `define __TINY_SYNTH_MULTI_CHANNEL_MIXER__ 3 | 4 | /* =================== 5 | * Two-into-one mixer 6 | * =================== 7 | * 8 | * Mixes up to 12 input signals into a single output. 9 | */ 10 | module multi_channel_mixer #( 11 | parameter DATA_BITS = 12, 12 | parameter ACTIVE_CHANNELS = 2 13 | ) 14 | ( 15 | input clk, 16 | input signed [DATA_BITS-1:0] a, 17 | input signed [DATA_BITS-1:0] b, 18 | input signed [DATA_BITS-1:0] c, 19 | input signed [DATA_BITS-1:0] d, 20 | input signed [DATA_BITS-1:0] e, 21 | input signed [DATA_BITS-1:0] f, 22 | input signed [DATA_BITS-1:0] g, 23 | input signed [DATA_BITS-1:0] h, 24 | input signed [DATA_BITS-1:0] i, 25 | input signed [DATA_BITS-1:0] j, 26 | input signed [DATA_BITS-1:0] k, 27 | input signed [DATA_BITS-1:0] l, 28 | output signed [DATA_BITS-1:0] dout 29 | ); 30 | 31 | localparam EXTRA_BITS_REQUIRED = $clog2(ACTIVE_CHANNELS); 32 | 33 | wire signed [DATA_BITS+4:0] sum; 34 | 35 | localparam MIN_VALUE = -(2**(DATA_BITS-1)); 36 | localparam MAX_VALUE = (2**(DATA_BITS-1))-1; 37 | 38 | assign sum = (a+b+c+d+e+f+g+h+i+j+k+l) >>> EXTRA_BITS_REQUIRED; 39 | assign dout = (sum < MIN_VALUE) 40 | ? MIN_VALUE : 41 | (sum > MAX_VALUE ? 42 | MAX_VALUE 43 | : sum); 44 | 45 | endmodule 46 | 47 | `endif 48 | -------------------------------------------------------------------------------- /hdl/pdm_dac.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_PDM_DAC__ 2 | `define __TINY_SYNTH_PDM_DAC__ 3 | /* ============================ 4 | * Pulse-density modulated DAC 5 | * ============================ 6 | * 7 | * This module drives a digital output at an average level equivalent 8 | * to the data-in (din) value. It can be filtered to an analog output 9 | * using a low-pass filter (eg. an RC filter). 10 | * 11 | * Principle of operation: 12 | * 13 | * This works by repeatedly adding the input (din) value to an accumulator of the 14 | * same width, and setting the output to "1" if the accumulator overflows. 15 | * The remainder after overflow is left in the accumulator for the next cycle, 16 | * and has the effect of averaging out any errors. 17 | * 18 | * (The accumulator has to be an extra bit wider than data-in to accomodate 19 | * the overflow (output) bit). 20 | */ 21 | module pdm_dac #(parameter DATA_BITS = 12)( 22 | input signed [DATA_BITS-1:0] din, 23 | input wire clk, 24 | output wire dout 25 | ); 26 | 27 | reg [DATA_BITS:0] accumulator; 28 | wire [DATA_BITS-1:0] unsigned_din; 29 | 30 | assign unsigned_din = din ^ (2**(DATA_BITS-1)); 31 | 32 | always @(posedge clk) begin 33 | accumulator <= (accumulator[DATA_BITS-1 : 0] + unsigned_din); 34 | end 35 | 36 | assign dout = accumulator[DATA_BITS]; 37 | 38 | endmodule 39 | 40 | `endif 41 | -------------------------------------------------------------------------------- /hdl/tiny-synth-all.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_ALL__ 2 | `define __TINY_SYNTH_ALL__ 3 | 4 | `ifndef __TINY_SYNTH_ROOT_FOLDER 5 | `define __TINY_SYNTH_ROOT_FOLDER ("..") 6 | `endif 7 | 8 | `include "clock_divider.vh" 9 | `include "amplitude_modulator.vh" 10 | `include "eight_bit_exponential_decay_lookup.vh" 11 | `include "envelope_generator.vh" 12 | `include "pdm_dac.vh" 13 | `include "tone_generator_noise.vh" 14 | `include "tone_generator_pulse.vh" 15 | `include "tone_generator_saw.vh" 16 | `include "tone_generator_triangle.vh" 17 | `include "tone_generator.vh" 18 | `include "two_into_one_mixer.vh" 19 | `include "multi_channel_mixer.vh" 20 | `include "voice.vh" 21 | `include "flanger.vh" 22 | `include "filter_ewma.vh" 23 | `include "filter_svf.vh" 24 | `include "filter_svf_pipelined.vh" 25 | 26 | `endif 27 | -------------------------------------------------------------------------------- /hdl/tone_generator.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TONE_GENERATOR_AGGREGATE__ 2 | `define __TINY_SYNTH_TONE_GENERATOR_AGGREGATE__ 3 | 4 | `include "tone_generator_saw.vh" 5 | `include "tone_generator_pulse.vh" 6 | `include "tone_generator_triangle.vh" 7 | `include "tone_generator_noise.vh" 8 | 9 | /* ================================ 10 | * Phase-accumulator tone-generator 11 | * ================================ 12 | * 13 | * This module aggregates the other tone generators together. 14 | * 15 | * It houses the accumulator and the logic for incrementing it 16 | * at a given frequency. 17 | * 18 | * It allows individual tone generators to be selected and logically 19 | * "ANDed" into the output stream. 20 | * 21 | * It also has provision for syncing oscillators together based on 22 | * when they overflow, and proving the accumulator MSB for ring 23 | * modulation purposes. 24 | */ 25 | module tone_generator #( 26 | parameter FREQ_BITS = 16, 27 | parameter PULSEWIDTH_BITS = 12, 28 | parameter OUTPUT_BITS = 12, 29 | parameter ACCUMULATOR_BITS = 24 30 | ) 31 | ( 32 | input [FREQ_BITS-1:0] tone_freq, 33 | input [PULSEWIDTH_BITS-1:0] pulse_width, 34 | input main_clk, 35 | input sample_clk, 36 | input rst, 37 | input test, 38 | output wire signed [OUTPUT_BITS-1:0] dout, 39 | output wire accumulator_msb, 40 | output wire sync_trigger_out, 41 | 42 | input wire en_ringmod, 43 | input wire ringmod_source, 44 | 45 | input wire en_sync, 46 | input wire sync_source, 47 | 48 | input en_noise, 49 | input en_pulse, 50 | input en_triangle, 51 | input en_saw); 52 | 53 | reg [ACCUMULATOR_BITS-1:0] accumulator; 54 | reg [ACCUMULATOR_BITS-1:0] prev_accumulator; 55 | 56 | wire [OUTPUT_BITS-1:0] noise_dout; 57 | tone_generator_noise #( 58 | .OUTPUT_BITS(OUTPUT_BITS) 59 | ) noise(.clk(accumulator[19]), .rst(rst || test), .dout(noise_dout)); 60 | 61 | wire [OUTPUT_BITS-1:0] triangle_dout; 62 | tone_generator_triangle #( 63 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 64 | .OUTPUT_BITS(OUTPUT_BITS) 65 | ) triangle_generator ( 66 | .accumulator(accumulator), 67 | .dout(triangle_dout), 68 | .en_ringmod(en_ringmod), 69 | .ringmod_source(ringmod_source) 70 | ); 71 | 72 | wire [OUTPUT_BITS-1:0] saw_dout; 73 | tone_generator_saw #( 74 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 75 | .OUTPUT_BITS(OUTPUT_BITS) 76 | ) saw( 77 | .accumulator(accumulator), 78 | .dout(saw_dout) 79 | ); 80 | 81 | wire [OUTPUT_BITS-1:0] pulse_dout; 82 | tone_generator_pulse #( 83 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS), 84 | .OUTPUT_BITS(OUTPUT_BITS), 85 | .PULSEWIDTH_BITS(PULSEWIDTH_BITS) 86 | ) pulse( 87 | .accumulator(accumulator), 88 | .dout(pulse_dout), 89 | .pulse_width(pulse_width) 90 | ); 91 | 92 | reg [OUTPUT_BITS-1:0] dout_tmp; 93 | 94 | always @(posedge main_clk) begin 95 | if ((en_sync && sync_source) || test) 96 | begin 97 | prev_accumulator <= 0; 98 | accumulator <= 0; 99 | end 100 | else 101 | begin 102 | prev_accumulator <= accumulator; 103 | accumulator <= accumulator + tone_freq; 104 | end 105 | end 106 | 107 | // ref ReSID: 108 | // msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000); 109 | // if (msb_rising && sync_dest->sync && !(sync && sync_source->msb_rising)) { 110 | // sync_dest->accumulator = 0; 111 | // } 112 | assign sync_trigger_out = (!(prev_accumulator & 24'h800000) && (accumulator & 24'h800000)); 113 | //&& (!(en_sync && sync_source)); 114 | 115 | assign accumulator_msb = accumulator[ACCUMULATOR_BITS-1]; 116 | 117 | always @(posedge sample_clk) begin 118 | dout_tmp = (2**OUTPUT_BITS)-1; 119 | if (en_noise) 120 | dout_tmp = dout_tmp & noise_dout; 121 | if (en_saw) 122 | dout_tmp = dout_tmp & saw_dout; 123 | if (en_triangle) 124 | dout_tmp = dout_tmp & triangle_dout; 125 | if (en_pulse) 126 | dout_tmp = dout_tmp & pulse_dout; 127 | end 128 | 129 | // convert dout value to a signed value 130 | assign dout = dout_tmp ^ (2**(OUTPUT_BITS-1)); 131 | 132 | endmodule 133 | 134 | `endif 135 | -------------------------------------------------------------------------------- /hdl/tone_generator_noise.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TONE_NOISE__ 2 | `define __TINY_SYNTH_TONE_NOISE__ 3 | 4 | /* ============================ 5 | * Random noise tone generator 6 | * ============================ 7 | * 8 | * This module creates a pseudo-random stream of noise at the rate of clk. 9 | * 10 | * out_data is 12-bit noise that is behaviourally similar to what the 6581 11 | * SID chip would produce. 12 | * 13 | * By varying the speed of the clk input, the pitch of the generated 14 | * noise can be varied. 15 | * 16 | * Principle of operation: 17 | * 18 | * The noise output is taken from intermediate bits of a 23-bit shift register 19 | * Operation: Calculate XOR result, shift register, set bit 0 = result. 20 | * 21 | * ----------------------->--------------------- 22 | * | | 23 | * ----EOR---- | 24 | * | | | 25 | * 2 2 2 1 1 1 1 1 1 1 1 1 1 | 26 | * Register bits: 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 <--- 27 | * | | | | | | | | 28 | * out bits : 7 6 5 4 3 2 1 0 29 | * 30 | * The 8-bits extracted from the shift register are then left-aligned 31 | * in the output bits. 32 | * 33 | * Note: Because of the way this works; if all of the bits in the shift 34 | * register somehow become zero's, the output will stay zero 35 | * permanently (0 XOR 0 => 0). 36 | * 37 | * For that reason the shfit register is initially seeded with a 38 | * "random" (key mashed, static) value, and can be reset with the rst 39 | * input if required. 40 | */ 41 | module tone_generator_noise #( 42 | parameter OUTPUT_BITS = 12 43 | )( 44 | input clk, 45 | input rst, 46 | output wire [OUTPUT_BITS-1:0] dout); 47 | 48 | reg [22:0] lsfr = 23'b01101110010010000101011; 49 | 50 | always @(posedge clk or posedge rst) begin 51 | if (rst) 52 | begin 53 | lsfr <= 23'b01101110010010000101011; 54 | end 55 | else 56 | begin 57 | lsfr <= { lsfr[21:0], lsfr[22] ^ lsfr[17] }; 58 | end 59 | end 60 | 61 | assign dout = { lsfr[22], lsfr[20], lsfr[16], lsfr[13], lsfr[11], lsfr[7], lsfr[4], lsfr[2], {(OUTPUT_BITS-8){1'b0}} }; 62 | 63 | endmodule 64 | 65 | `endif 66 | -------------------------------------------------------------------------------- /hdl/tone_generator_pulse.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TONE_PULSE__ 2 | `define __TINY_SYNTH_TONE_PULSE__ 3 | /* ============================= 4 | * Pulse (square) tone generator 5 | * ============================= 6 | * 7 | * Generates a pulse-width modulated output according to accumulator value, at a duty 8 | * cycle determined by the pulse_width input. 9 | * 10 | * Principle of operation: 11 | * 12 | * If accumulator[MSB] <= pulse_width, then the output will be full-scale; 13 | * If accumulator[MSB] > pulse_width, then the output will be zero. 14 | * 15 | * Setting pulse_width to half of it's full-scale value will result in a 16 | * roughly square wave out. Varying the value will result in pulse outputs with 17 | * varying duty cycles. 18 | */ 19 | module tone_generator_pulse #( 20 | parameter ACCUMULATOR_BITS = 24, 21 | parameter PULSEWIDTH_BITS = 12, 22 | parameter OUTPUT_BITS = 12) 23 | ( 24 | input [ACCUMULATOR_BITS-1:0] accumulator, 25 | input [PULSEWIDTH_BITS-1:0] pulse_width, 26 | output wire [OUTPUT_BITS-1:0] dout); 27 | 28 | localparam MAX_SCALE = (2**OUTPUT_BITS) - 1; 29 | 30 | // if accumulator value > pulse_width, output = MAX; else 0; 31 | assign dout = (accumulator[ACCUMULATOR_BITS-1 -: PULSEWIDTH_BITS] <= pulse_width) ? MAX_SCALE : 0; 32 | 33 | endmodule 34 | 35 | `endif 36 | -------------------------------------------------------------------------------- /hdl/tone_generator_saw.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TONE_SAW__ 2 | `define __TINY_SYNTH_TONE_SAW__ 3 | /* ============================= 4 | * Sawtooth tone generator 5 | * ============================= 6 | * 7 | * Generates a sawtooth output waveform. 8 | * 9 | * Principle of operation: 10 | * 11 | * Take the upper OUTPUT_BITS from the accumulator. 12 | */ 13 | module tone_generator_saw #( 14 | parameter ACCUMULATOR_BITS = 24, 15 | parameter OUTPUT_BITS = 12) 16 | ( 17 | input [ACCUMULATOR_BITS-1:0] accumulator, 18 | output wire [OUTPUT_BITS-1:0] dout); 19 | 20 | assign dout = accumulator[ACCUMULATOR_BITS-1 -: OUTPUT_BITS]; 21 | 22 | endmodule 23 | 24 | `endif 25 | -------------------------------------------------------------------------------- /hdl/tone_generator_triangle.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TONE_TRIANGLE__ 2 | `define __TINY_SYNTH_TONE_TRIANGLE__ 3 | /* ============================= 4 | * Triangle tone generator 5 | * ============================= 6 | * 7 | * Generates a triangle-wave; optionally ring-modulated with the MSB of 8 | * another oscillator. 9 | * 10 | * Principle of operation: 11 | * 12 | * Since we want the triangle wave to count up, and back down while the 13 | * accumulator transitions from 0 to full-scale, we need a way of inverting 14 | * the wave at half-scale. 15 | * 16 | * It turns out that binary inverting a counter makes it count in the 17 | * reverse direction, so we use the accumulator MSB to optionally invert 18 | * the bottom bits of the accumulator, which are then shifted into the 19 | * output. 20 | * 21 | * The "inversion" of the counter can also be driven by the MSB 22 | * from another oscillator, and in SID terms this is known as "ring modulation". 23 | * This allows for some quite complex waveforms to be generated by connecting 24 | * multiple voices together. 25 | */ 26 | module tone_generator_triangle #( 27 | parameter ACCUMULATOR_BITS = 24, 28 | parameter OUTPUT_BITS = 12) 29 | ( 30 | input [ACCUMULATOR_BITS-1:0] accumulator, 31 | output wire [OUTPUT_BITS-1:0] dout, 32 | input en_ringmod, 33 | input ringmod_source); 34 | 35 | wire invert_wave; 36 | 37 | // invert the waveform (ie. start counting down instead of up) 38 | // if either ringmod is enabled and high, 39 | // or MSB of accumulator is set. 40 | assign invert_wave = (en_ringmod && ringmod_source) 41 | || (!en_ringmod && accumulator[ACCUMULATOR_BITS-1]); 42 | 43 | assign dout = invert_wave ? ~accumulator[ACCUMULATOR_BITS-2 -: OUTPUT_BITS] 44 | : accumulator[ACCUMULATOR_BITS-2 -: OUTPUT_BITS]; 45 | 46 | endmodule 47 | 48 | `endif 49 | -------------------------------------------------------------------------------- /hdl/two_into_one_mixer.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_TWO_INTO_ONE_MIXER__ 2 | `define __TINY_SYNTH_TWO_INTO_ONE_MIXER__ 3 | 4 | /* =================== 5 | * Two-into-one mixer 6 | * =================== 7 | * 8 | * Mixes two input signals into a single output. 9 | */ 10 | module two_into_one_mixer #( 11 | parameter DATA_BITS = 12 12 | ) 13 | ( 14 | input signed [DATA_BITS-1:0] a, 15 | input signed [DATA_BITS-1:0] b, 16 | output signed [DATA_BITS-1:0] dout 17 | ); 18 | 19 | wire signed [DATA_BITS:0] intermediate; 20 | 21 | assign intermediate = a+b; 22 | 23 | assign dout = intermediate >>> 1; 24 | 25 | endmodule 26 | 27 | `endif 28 | -------------------------------------------------------------------------------- /hdl/voice.vh: -------------------------------------------------------------------------------- 1 | `ifndef __TINY_SYNTH_VOICE__ 2 | `define __TINY_SYNTH_VOICE__ 3 | 4 | `include "tone_generator.vh" 5 | `include "envelope_generator.vh" 6 | `include "amplitude_modulator.vh" 7 | 8 | module voice #( 9 | parameter OUTPUT_BITS = 12, 10 | parameter FREQ_BITS = 16, 11 | parameter PULSEWIDTH_BITS = 12, 12 | parameter ACCUMULATOR_BITS = 24, 13 | parameter SAMPLE_CLK_FREQ = 44100 14 | )( 15 | input [FREQ_BITS-1:0] tone_freq, 16 | input [3:0] waveform_enable, 17 | input [PULSEWIDTH_BITS-1:0] pulse_width, 18 | input main_clk, 19 | input sample_clk, 20 | input rst, 21 | input test, 22 | output wire signed [OUTPUT_BITS-1:0] dout, 23 | output wire accumulator_msb, /* used to feed ringmod on another voice */ 24 | output wire sync_trigger_out, /* used to sync with another oscillator */ 25 | input en_ringmod, 26 | input wire ringmod_source, 27 | input en_sync, 28 | input wire sync_source, 29 | 30 | // envelope generator params 31 | input wire gate, 32 | input [3:0] attack, 33 | input [3:0] decay, 34 | input [3:0] sustain, 35 | input [3:0] rel 36 | ); 37 | 38 | wire signed [OUTPUT_BITS-1:0] tone_generator_data; 39 | wire[7:0] envelope_amplitude; 40 | 41 | tone_generator #( 42 | .FREQ_BITS(FREQ_BITS), 43 | .PULSEWIDTH_BITS(PULSEWIDTH_BITS), 44 | .OUTPUT_BITS(OUTPUT_BITS), 45 | .ACCUMULATOR_BITS(ACCUMULATOR_BITS) 46 | ) tone_generator ( 47 | .tone_freq(tone_freq), 48 | .en_noise(waveform_enable[3]), 49 | .en_pulse(waveform_enable[2]), 50 | .en_saw(waveform_enable[1]), 51 | .en_triangle(waveform_enable[0]), 52 | .pulse_width(pulse_width), 53 | .main_clk(main_clk), 54 | .sample_clk(sample_clk), 55 | .rst(rst), 56 | .test(test), 57 | .dout(tone_generator_data), 58 | .accumulator_msb(accumulator_msb), 59 | .sync_trigger_out(sync_trigger_out), 60 | .en_sync(en_sync), 61 | .sync_source(sync_source), 62 | .en_ringmod(en_ringmod), 63 | .ringmod_source(ringmod_source) 64 | ); 65 | 66 | envelope_generator #( 67 | .SAMPLE_CLK_FREQ(SAMPLE_CLK_FREQ) 68 | ) 69 | envelope( 70 | .clk(sample_clk), 71 | .rst(rst), 72 | .gate(gate), 73 | .a(attack), 74 | .d(decay), 75 | .s(sustain), 76 | .r(rel), 77 | .amplitude(envelope_amplitude) 78 | ); 79 | 80 | amplitude_modulator #(.DATA_BITS(OUTPUT_BITS)) modulator( 81 | .clk(sample_clk), 82 | .din(tone_generator_data), 83 | .amplitude(envelope_amplitude), 84 | .dout(dout) 85 | ); 86 | 87 | endmodule 88 | 89 | `endif 90 | -------------------------------------------------------------------------------- /test/clock_divider/clock_divider.v: -------------------------------------------------------------------------------- 1 | // simple instantiation of the clock divider module 2 | `include "../../hdl/clock_divider.vh" 3 | -------------------------------------------------------------------------------- /test/clock_divider/clock_divider_tb.v: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------- 2 | //-- Testbench for the tiny-synth clock divider module 3 | //------------------------------------------------------------------- 4 | `default_nettype none 5 | `timescale 100 ns / 10 ns 6 | 7 | module clock_divider_tb(); 8 | 9 | //-- Simulation time: 1us (10 * 100ns) 10 | parameter DURATION = 100; 11 | 12 | //-- Clock signal. Running at 1MHz 13 | reg clkin = 0; 14 | always #0.5 clkin = ~clkin; 15 | 16 | wire clkoutdiv1, clkoutdiv2, clkoutdiv3, clkoutdiv4, clkoutdiv5, clkoutdiv6, clkoutdiv7, clkoutdiv8; 17 | 18 | //-- Instantiate the unit to test 19 | //clock_divider #(.DIVISOR(1)) UUT1(clkin, clkoutdiv1); 20 | clock_divider #(.DIVISOR(2)) UUT2(clkin, clkoutdiv2); 21 | clock_divider #(.DIVISOR(3)) UUT3(clkin, clkoutdiv3); 22 | clock_divider #(.DIVISOR(4)) UUT4(clkin, clkoutdiv4); 23 | clock_divider #(.DIVISOR(5)) UUT5(clkin, clkoutdiv5); 24 | clock_divider #(.DIVISOR(6)) UUT6(clkin, clkoutdiv6); 25 | clock_divider #(.DIVISOR(7)) UUT7(clkin, clkoutdiv7); 26 | clock_divider #(.DIVISOR(8)) UUT8(clkin, clkoutdiv8); 27 | 28 | initial begin 29 | 30 | //-- File were to store the simulation results 31 | $dumpfile("clock_divider_tb.vcd"); 32 | $dumpvars(0, clock_divider_tb); 33 | 34 | #(DURATION) $display("End of simulation"); 35 | $finish; 36 | end 37 | 38 | endmodule 39 | -------------------------------------------------------------------------------- /test/envelope_generator/envelope_generator.v: -------------------------------------------------------------------------------- 1 | // simple instantiation of the ewma filter module 2 | `ifndef __TINY_SYNTH_ROOT_FOLDER 3 | `define __TINY_SYNTH_ROOT_FOLDER ("../..") 4 | `endif 5 | 6 | `include "../../hdl/eight_bit_exponential_decay_lookup.vh" 7 | `include "../../hdl/envelope_generator.vh" 8 | `include "../../hdl/clock_divider.vh" 9 | -------------------------------------------------------------------------------- /test/envelope_generator/envelope_generator_tb.v: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------- 2 | //-- Testbench for the tiny-synth clock divider module 3 | //------------------------------------------------------------------- 4 | `default_nettype none 5 | `timescale 100ns / 10ns 6 | 7 | `include "envelope_generator.v" 8 | 9 | module envelope_generator_tb(); 10 | 11 | //-- Simulation time: Duration * 1us (timescale above) 12 | parameter DURATION = 88200; // 2 second worth of samples 13 | 14 | //-- Clock signal. Running at 1MHz 15 | reg clkin = 0; 16 | always #0.5 clkin = ~clkin; 17 | 18 | reg gate = 1; 19 | always #22050 gate = 0; 20 | 21 | wire[7:0] envelope; 22 | 23 | envelope_generator DUT( 24 | .rst(1'b0), 25 | .clk(clkin), 26 | .gate(gate), 27 | .a(4'd4), .d(4'd4), .s(4'd8), .r(4'd4), 28 | .amplitude(envelope) 29 | ); 30 | 31 | initial begin 32 | 33 | //-- File were to store the simulation results 34 | $dumpfile("envelope_generator_tb.vcd"); 35 | $dumpvars(0, envelope_generator_tb); 36 | 37 | #(DURATION) $display("End of simulation"); 38 | $finish; 39 | end 40 | 41 | endmodule 42 | -------------------------------------------------------------------------------- /test/filter_ewma/filter_ewma.v: -------------------------------------------------------------------------------- 1 | // simple instantiation of the ewma filter module 2 | `ifndef __TINY_SYNTH_ROOT_FOLDER 3 | `define __TINY_SYNTH_ROOT_FOLDER ("../..") 4 | `endif 5 | 6 | `include "../../hdl/filter_ewma.vh" 7 | `include "../../hdl/clock_divider.vh" 8 | -------------------------------------------------------------------------------- /test/filter_ewma/filter_ewma_tb.v: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------- 2 | //-- Testbench for the tiny-synth clock divider module 3 | //------------------------------------------------------------------- 4 | `default_nettype none 5 | `timescale 100 ns / 10 ns 6 | 7 | module filter_ewma_tb(); 8 | 9 | //-- Simulation time: Duration * 0.1us (timescale above) 10 | parameter DURATION = 1000; // 1000 = 0.1 milliseconds 11 | 12 | //-- Clock signal. Running at 1MHz 13 | reg clkin = 0; 14 | always #0.5 clkin = ~clkin; 15 | 16 | wire sq_wave_clk; 17 | wire signed [11:0] sq_wave_sig; 18 | 19 | clock_divider #(.DIVISOR(128)) cdiv_sq(.cin(clkin), .cout(sq_wave_clk)); 20 | 21 | assign sq_wave_sig = sq_wave_clk ? -12'd2048 : 12'd2047; 22 | 23 | wire signed [11:0] filter_out; 24 | filter_ewma moving_average_filter(.clk(clkin), .s_alpha($signed(9'd30)), .din(sq_wave_sig), .dout(filter_out)); 25 | 26 | initial begin 27 | 28 | //-- File were to store the simulation results 29 | $dumpfile("filter_ewma_tb.vcd"); 30 | $dumpvars(0, filter_ewma_tb); 31 | 32 | #(DURATION) $display("End of simulation"); 33 | $finish; 34 | end 35 | 36 | endmodule 37 | -------------------------------------------------------------------------------- /test/filter_svf/filter_svf.v: -------------------------------------------------------------------------------- 1 | // simple instantiation of the ewma filter module 2 | `ifndef __TINY_SYNTH_ROOT_FOLDER 3 | `define __TINY_SYNTH_ROOT_FOLDER ("../..") 4 | `endif 5 | 6 | `include "../../hdl/filter_svf.vh" 7 | `include "../../hdl/clock_divider.vh" 8 | -------------------------------------------------------------------------------- /test/filter_svf/filter_svf_tb.v: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------- 2 | //-- Testbench for the tiny-synth clock divider module 3 | //------------------------------------------------------------------- 4 | `default_nettype none 5 | `timescale 100 ns / 10 ns 6 | 7 | module filter_svf_tb(); 8 | 9 | //-- Simulation time: Duration * 0.1us (timescale above) 10 | parameter DURATION = 1000; // 1000 = 0.1 milliseconds 11 | 12 | //-- Clock signal. Running at 1MHz 13 | reg clkin = 0; 14 | always #0.5 clkin = ~clkin; 15 | 16 | wire sq_wave_clk; 17 | wire signed [11:0] sq_wave_sig; 18 | 19 | clock_divider #(.DIVISOR(128)) cdiv_sq(.cin(clkin), .cout(sq_wave_clk)); 20 | 21 | assign sq_wave_sig = sq_wave_clk ? -12'd2048 : 12'd2047; 22 | 23 | wire signed [11:0] high_pass_out; 24 | wire signed [11:0] low_pass_out; 25 | wire signed [11:0] band_pass_out; 26 | wire signed [11:0] notch_pass_out; 27 | 28 | localparam Q = 4.0; 29 | localparam Q1 = 1.0/Q; 30 | localparam Q1_fixed_point = $rtoi(Q1 * (2**16)); 31 | 32 | localparam F = 0.4; 33 | localparam F_fixed_point = $rtoi(F * (2**17)); 34 | 35 | filter_svf state_variable_filter( 36 | .clk(clkin), 37 | .F(F_fixed_point), 38 | .Q1(Q1_fixed_point), 39 | .in(sq_wave_sig), 40 | .out_highpass(high_pass_out), 41 | .out_lowpass(low_pass_out), 42 | .out_bandpass(band_pass_out), 43 | .out_notch(notch_pass_out) 44 | ); 45 | 46 | initial begin 47 | 48 | //-- File were to store the simulation results 49 | $dumpfile("filter_svf_tb.vcd"); 50 | $dumpvars(0, filter_svf_tb); 51 | 52 | #(DURATION) $display("End of simulation"); 53 | $finish; 54 | end 55 | 56 | endmodule 57 | -------------------------------------------------------------------------------- /test/filter_svf_pipelined/filter_svf.v: -------------------------------------------------------------------------------- 1 | // simple instantiation of the ewma filter module 2 | `ifndef __TINY_SYNTH_ROOT_FOLDER 3 | `define __TINY_SYNTH_ROOT_FOLDER ("../..") 4 | `endif 5 | 6 | `include "../../hdl/filter_svf_pipelined.vh" 7 | `include "../../hdl/clock_divider.vh" 8 | -------------------------------------------------------------------------------- /test/filter_svf_pipelined/filter_svf_tb.v: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------- 2 | //-- Testbench for the tiny-synth clock divider module 3 | //------------------------------------------------------------------- 4 | `default_nettype none 5 | `timescale 100 ns / 10 ns 6 | 7 | module filter_svf_tb(); 8 | 9 | //-- Simulation time: Duration * 0.1us (timescale above) 10 | parameter DURATION = 1000; // 1000 = 0.1 milliseconds 11 | 12 | //-- Clock signal. Running at 1MHz 13 | reg clkin = 1'b1; 14 | always #0.5 clkin = ~clkin; 15 | 16 | wire clkin8; 17 | wire sq_wave_clk; 18 | wire signed [11:0] sq_wave_sig; 19 | 20 | clock_divider #(.DIVISOR(1024)) cdiv_sq(.cin(clkin), .cout(sq_wave_clk)); 21 | clock_divider #(.DIVISOR(16)) cdiv_c8(.cin(clkin), .cout(clkin8)); 22 | 23 | assign sq_wave_sig = sq_wave_clk ? -12'd2048 : 12'd2047; 24 | 25 | wire signed [11:0] high_pass_out; 26 | wire signed [11:0] low_pass_out; 27 | wire signed [11:0] band_pass_out; 28 | wire signed [11:0] notch_pass_out; 29 | 30 | localparam Q = 4.0; 31 | localparam Q1 = 1.0/Q; 32 | localparam Q1_fixed_point = $rtoi(Q1 * (2**16)); 33 | 34 | localparam F = 0.4; 35 | localparam F_fixed_point = $rtoi(F * (2**17)); 36 | 37 | filter_svf_pipelined state_variable_filter( 38 | .sample_clk(clkin8), 39 | .clk(clkin), 40 | .F(F_fixed_point), 41 | .Q1(Q1_fixed_point), 42 | .in(sq_wave_sig), 43 | .out_highpass(high_pass_out), 44 | .out_lowpass(low_pass_out), 45 | .out_bandpass(band_pass_out), 46 | .out_notch(notch_pass_out) 47 | ); 48 | 49 | initial begin 50 | 51 | //-- File were to store the simulation results 52 | $dumpfile("filter_svf_tb.vcd"); 53 | $dumpvars(0, filter_svf_tb); 54 | 55 | #(DURATION) $display("End of simulation"); 56 | $finish; 57 | end 58 | 59 | endmodule 60 | --------------------------------------------------------------------------------