├── .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 |
--------------------------------------------------------------------------------