├── .gitignore ├── 7seg_count └── 7seg_count.py ├── README.md ├── blink └── blink.py ├── pwm_fade └── pwm_fade.py ├── rgb_fade └── rgb_fade.py ├── rgb_fade_gamma └── rgb_fade.py ├── tristate_blink └── tristate_blink.py ├── tristate_fade └── tristate_fade.py └── uart └── uart.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.vcd 3 | -------------------------------------------------------------------------------- /7seg_count/7seg_count.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.build.generic_platform import Pins 3 | from migen.build.platforms import icebreaker 4 | 5 | class SevenSegCounter(Module): 6 | def __init__(self, segment_pins, digit_sel): 7 | segment_count = len(segment_pins) 8 | segments = Signal(segment_count) 9 | counter = Signal(30) 10 | ones = Signal(4) 11 | tens = Signal(4) 12 | ones_segments = Signal(segment_count) 13 | tens_segments = Signal(segment_count) 14 | display_state = Signal(3) 15 | 16 | # Segment pins are active-low so invert here 17 | self.comb += segment_pins.eq(~segments) 18 | self.comb += ones.eq(counter[21:25]) 19 | self.comb += tens.eq(counter[25:30]) 20 | self.comb += display_state.eq(counter[2:5]) 21 | self.digit_to_segments(ones, ones_segments) 22 | self.digit_to_segments(tens, tens_segments) 23 | 24 | self.sync += counter.eq(counter + 1) 25 | self.sync += Case(display_state, { 26 | 0: segments.eq(ones_segments), 27 | 1: segments.eq(ones_segments), 28 | 2: segments.eq(0), 29 | 3: digit_sel.eq(0), 30 | 4: segments.eq(tens_segments), 31 | 5: segments.eq(tens_segments), 32 | 6: segments.eq(0), 33 | 7: digit_sel.eq(1), 34 | }) 35 | 36 | def digit_to_segments(self, digit, segments): 37 | self.comb += Case(digit, { 38 | 0x0: segments.eq(0b0111111), 39 | 0x1: segments.eq(0b0000110), 40 | 0x2: segments.eq(0b1011011), 41 | 0x3: segments.eq(0b1001111), 42 | 0x4: segments.eq(0b1100110), 43 | 0x5: segments.eq(0b1101101), 44 | 0x6: segments.eq(0b1111101), 45 | 0x7: segments.eq(0b0000111), 46 | 0x8: segments.eq(0b1111111), 47 | 0x9: segments.eq(0b1101111), 48 | 0xa: segments.eq(0b1110111), 49 | 0xb: segments.eq(0b1111100), 50 | 0xc: segments.eq(0b0111001), 51 | 0xd: segments.eq(0b1011110), 52 | 0xe: segments.eq(0b1111001), 53 | 0xf: segments.eq(0b1110001), 54 | }) 55 | 56 | sevenseg_pmod = [ 57 | ("segments", 0, Pins(" ".join(["PMOD1A:" + str(i) for i in range(7)]))), 58 | ("digit_sel", 0, Pins("PMOD1A:7")), 59 | ] 60 | 61 | plat = icebreaker.Platform() 62 | plat.add_extension(sevenseg_pmod) 63 | segments = plat.request("segments") 64 | digit_sel = plat.request("digit_sel") 65 | my_counter = SevenSegCounter(segments, digit_sel) 66 | plat.build(my_counter) 67 | plat.create_programmer().flash(0, 'build/top.bin') 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iCEBreaker migen examples 2 | 3 | This repository contains examples for the [migen](https://github.com/m-labs/migen) Python toolbox for building complex digital hardware. 4 | 5 | You will need to have python3, migen and icestorm/nextpnr/yosys installed on your system. 6 | 7 | After that all you need to do is connect your iCEBreaker to the computer and run the python script in an example directory. 8 | 9 | The scripts are by default set to synthesize and upload the bitstream to the iCEBreaker board. 10 | -------------------------------------------------------------------------------- /blink/blink.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.build.platforms import icebreaker 3 | 4 | class Blinker(Module): 5 | def __init__(self, led, maxperiod): 6 | counter = Signal(max=maxperiod+1) 7 | period = Signal(max=maxperiod+1) 8 | self.comb += period.eq(maxperiod) 9 | self.sync += If(counter == 0, 10 | led.eq(~led), 11 | counter.eq(period) 12 | ).Else( 13 | counter.eq(counter - 1) 14 | ) 15 | 16 | plat = icebreaker.Platform() 17 | led = plat.request("user_ledr_n") 18 | my_blinker = Blinker(led, 10000000) 19 | plat.build(my_blinker) 20 | plat.create_programmer().flash(0, 'build/top.bin') 21 | -------------------------------------------------------------------------------- /pwm_fade/pwm_fade.py: -------------------------------------------------------------------------------- 1 | # Small example fading the red led on the board up and down using PWM 2 | 3 | from migen import * 4 | from migen.build.platforms import icebreaker 5 | 6 | class PWM(Module): 7 | def __init__(self, pwm, bitwidth, value): 8 | pwm_counter = Signal(bitwidth) 9 | self.comb += pwm.eq(pwm_counter < value) 10 | self.sync += pwm_counter.eq(pwm_counter + 1) 11 | 12 | class UpdownCounter(Module): 13 | def __init__(self, counter, bitwidth): 14 | icounter = Signal(bitwidth+1) 15 | direction = Signal() 16 | 17 | self.comb += direction.eq(icounter[bitwidth]) 18 | self.comb += If(direction, 19 | counter.eq(~icounter[0:bitwidth]) 20 | ).Else( 21 | counter.eq( icounter[0:bitwidth])) 22 | 23 | icounter_inv = Signal(bitwidth) 24 | self.comb += icounter_inv.eq(~icounter[0:bitwidth]) 25 | self.sync += If(icounter_inv == 0, 26 | icounter.eq(icounter + 2) 27 | ).Else( 28 | icounter.eq(icounter + 1)) 29 | 30 | class TickUpdownCounter(Module): 31 | def __init__(self, counter, tick, bitwidth): 32 | icounter = Signal(bitwidth+1) 33 | direction = Signal() 34 | 35 | self.comb += direction.eq(icounter[bitwidth]) 36 | self.comb += If(direction, 37 | counter.eq(~icounter[0:bitwidth]) 38 | ).Else( 39 | counter.eq( icounter[0:bitwidth])) 40 | 41 | icounter_inv = Signal(bitwidth) 42 | self.comb += icounter_inv.eq(~icounter[0:bitwidth]) 43 | self.sync += If(tick, 44 | If((icounter_inv) == 0, 45 | icounter.eq(icounter + 2) 46 | ).Else( 47 | icounter.eq(icounter + 1))) 48 | 49 | class ClockDiv(Module): 50 | def __init__(self, divbitwidth, divout, divtick): 51 | divcounter = Signal(divbitwidth+1) 52 | # count every clock tick 53 | self.sync += divcounter.eq(divcounter + 1) 54 | # output 50% duty cycle clock output 55 | self.comb += divout.eq(divcounter[divbitwidth]) 56 | # output a one clock wide strobe 57 | divcounter_inv = Signal(divbitwidth) 58 | self.comb += divcounter_inv.eq(~divcounter[0:divbitwidth]) 59 | self.comb += divtick.eq(divcounter_inv == 0) 60 | 61 | class PWMFade(Module): 62 | def __init__(self, pwm_signal, dbg, width, div): 63 | pwm_value = Signal(width) 64 | self.submodules.pwm = PWM(pwm_signal, width, pwm_value) 65 | 66 | updown_clock = Signal() 67 | updown_clock_strobe = Signal() 68 | self.submodules.updown_clk_div = \ 69 | ClockDiv(div, updown_clock, updown_clock_strobe) 70 | 71 | self.submodules.updown = \ 72 | TickUpdownCounter(pwm_value, updown_clock_strobe, width) 73 | self.comb += dbg.eq(updown_clock) 74 | 75 | def _test(dut): 76 | for i in range(1000): 77 | yield 78 | 79 | if __name__ == "__main__": 80 | import sys 81 | if len(sys.argv) != 1: 82 | if sys.argv[1] == "sim": 83 | pwm_out = Signal() 84 | dbg_sig = Signal() 85 | dut = PWMFade(pwm_out, dbg_sig, 4, 4) 86 | dut.clock_domains.cd_sys = ClockDomain("sys") 87 | run_simulation(dut, _test(dut), vcd_name="pwm_fade.vcd") 88 | else: 89 | plat = icebreaker.Platform() 90 | led = plat.request("user_ledr_n") 91 | dbg_led = plat.request("user_ledg_n") 92 | pwm_fade = PWMFade(led, dbg_led, 16, 9) 93 | plat.build(pwm_fade) 94 | plat.create_programmer().flash(0, 'build/top.bin') 95 | -------------------------------------------------------------------------------- /rgb_fade/rgb_fade.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.build.generic_platform import Pins 3 | from migen.build.platforms import icebreaker 4 | 5 | class RGBFade(Module): 6 | def __init__(self, leds): 7 | pwm_width = 12 8 | ctr_width = 28 9 | 10 | ctr = Signal(ctr_width) 11 | col = Signal(3) 12 | direction = Signal() 13 | fade = Signal(pwm_width) 14 | self.comb += col.eq(ctr[ctr_width-3:ctr_width]) 15 | self.comb += direction.eq(ctr[ctr_width-4]) 16 | self.comb += fade.eq(ctr[ctr_width-4-pwm_width:ctr_width-4]) 17 | 18 | r_val = Signal(pwm_width) 19 | g_val = Signal(pwm_width) 20 | b_val = Signal(pwm_width) 21 | pwm_r = Signal() 22 | pwm_g = Signal() 23 | pwm_b = Signal() 24 | pwm_ctr = Signal(pwm_width) 25 | 26 | # Fade up and down for each combination of colours 27 | self.sync += ctr.eq(ctr + 1) 28 | self.sync += r_val.eq(Mux(col[0], Mux(direction, ~fade, fade), 0)) 29 | self.sync += g_val.eq(Mux(col[1], Mux(direction, ~fade, fade), 0)) 30 | self.sync += b_val.eq(Mux(col[2], Mux(direction, ~fade, fade), 0)) 31 | 32 | # PWM 33 | self.sync += pwm_ctr.eq(pwm_ctr + 1) 34 | self.sync += pwm_r.eq(pwm_ctr < r_val) 35 | self.sync += pwm_g.eq(pwm_ctr < g_val) 36 | self.sync += pwm_b.eq(pwm_ctr < b_val) 37 | self.specials += Instance("SB_RGBA_DRV", 38 | i_CURREN=1, 39 | i_RGBLEDEN=1, 40 | i_RGB0PWM=pwm_r, 41 | i_RGB1PWM=pwm_g, 42 | i_RGB2PWM=pwm_b, 43 | o_RGB0=leds[0], 44 | o_RGB1=leds[1], 45 | o_RGB2=leds[2], 46 | p_CURRENT_MODE="0b1", 47 | p_RGB0_CURRENT="0b000111", 48 | p_RGB1_CURRENT="0b000111", 49 | p_RGB2_CURRENT="0b000111", 50 | ) 51 | 52 | rgb_led = [ 53 | ("red", 0, Pins("39")), 54 | ("green", 0, Pins("40")), 55 | ("blue", 0, Pins("41")), 56 | ] 57 | 58 | plat = icebreaker.Platform() 59 | plat.add_extension(rgb_led) 60 | leds = [plat.request(led) for led in ["red", "green", "blue"]] 61 | rgb_fade = RGBFade(leds) 62 | plat.build(rgb_fade) 63 | plat.create_programmer().flash(0, 'build/top.bin') 64 | -------------------------------------------------------------------------------- /rgb_fade_gamma/rgb_fade.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.build.generic_platform import Pins 3 | from migen.build.platforms import icebreaker 4 | 5 | class RGBFadeGamma(Module): 6 | def __init__(self, leds): 7 | pwm_width = 16 8 | fade_width = 8 9 | ctr_width = 28 10 | 11 | ctr = Signal(ctr_width) 12 | col = Signal(3) 13 | direction = Signal() 14 | fade = Signal(fade_width) 15 | self.comb += col.eq(ctr[ctr_width-3:ctr_width]) 16 | self.comb += direction.eq(ctr[ctr_width-4]) 17 | self.comb += fade.eq(ctr[ctr_width-4-fade_width:ctr_width-4]) 18 | 19 | fade_corr = Signal(pwm_width) 20 | r_val = Signal(pwm_width) 21 | g_val = Signal(pwm_width) 22 | b_val = Signal(pwm_width) 23 | pwm_r = Signal() 24 | pwm_g = Signal() 25 | pwm_b = Signal() 26 | pwm_ctr = Signal(pwm_width) 27 | 28 | # Gamma correction 29 | self.specials.mem = Memory(16, 256, init=self.gen_gamma_table(256)) 30 | p = self.mem.get_port() 31 | self.specials += p 32 | self.comb += p.adr.eq(fade) 33 | self.comb += fade_corr.eq(p.dat_r) 34 | 35 | # Fading up and down for each combination of colours 36 | self.sync += ctr.eq(ctr + 1) 37 | self.sync += r_val.eq(Mux(col[0], Mux(direction, ~fade_corr, fade_corr), 0)) 38 | self.sync += g_val.eq(Mux(col[1], Mux(direction, ~fade_corr, fade_corr), 0)) 39 | self.sync += b_val.eq(Mux(col[2], Mux(direction, ~fade_corr, fade_corr), 0)) 40 | 41 | # PWM 42 | self.sync += pwm_ctr.eq(pwm_ctr + 1) 43 | self.sync += pwm_r.eq(pwm_ctr < r_val) 44 | self.sync += pwm_g.eq(pwm_ctr < g_val) 45 | self.sync += pwm_b.eq(pwm_ctr < b_val) 46 | self.specials += Instance("SB_RGBA_DRV", 47 | i_CURREN=1, 48 | i_RGBLEDEN=1, 49 | i_RGB0PWM=pwm_r, 50 | i_RGB1PWM=pwm_g, 51 | i_RGB2PWM=pwm_b, 52 | o_RGB0=leds[0], 53 | o_RGB1=leds[1], 54 | o_RGB2=leds[2], 55 | p_CURRENT_MODE="0b1", 56 | p_RGB0_CURRENT="0b000001", 57 | p_RGB1_CURRENT="0b000001", 58 | p_RGB2_CURRENT="0b000001", 59 | ) 60 | 61 | def gen_gamma_table(self, n): 62 | gamma = 2.2 63 | return [int(0xFFFF * pow((1.0 / 255.0) * i, gamma)) for i in range(n)] 64 | 65 | rgb_led = [ 66 | ("red", 0, Pins("39")), 67 | ("green", 0, Pins("40")), 68 | ("blue", 0, Pins("41")), 69 | ] 70 | 71 | plat = icebreaker.Platform() 72 | plat.add_extension(rgb_led) 73 | leds = [plat.request(led) for led in ["red", "green", "blue"]] 74 | rgb_fade = RGBFadeGamma(leds) 75 | plat.build(rgb_fade) 76 | plat.create_programmer().flash(0, 'build/top.bin') 77 | -------------------------------------------------------------------------------- /tristate_blink/tristate_blink.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.build.generic_platform import * 3 | from migen.build.platforms import icebreaker 4 | 5 | class TristatePins(Module): 6 | def __init__(self, n, pad, on, hiz): 7 | self.t = [] 8 | for i in range(n): 9 | self.t.append(TSTriple()) 10 | self.specials += self.t[i].get_tristate(pad[i]) 11 | self.comb += self.t[i].o.eq(on[i]) 12 | self.comb += self.t[i].oe.eq(~hiz[i]) 13 | 14 | class Blinker(Module): 15 | def __init__(self, led, maxperiod): 16 | counter = Signal(max=maxperiod+1) 17 | period = Signal(max=maxperiod+1) 18 | state_counter = Signal(2); 19 | 20 | # Timer 21 | self.comb += period.eq(maxperiod) 22 | self.sync += If(counter == 0, 23 | state_counter.eq(state_counter + 1), 24 | counter.eq(period) 25 | ).Else( 26 | counter.eq(counter - 1) 27 | ) 28 | 29 | # LEDs 30 | on = [Signal() for l in led] 31 | hiz = [Signal() for l in led] 32 | self.submodules.tri = TristatePins(len(led), led, on, hiz) 33 | for h in hiz: 34 | self.comb += h.eq(state_counter[0]) 35 | for o in on: 36 | self.comb += o.eq(state_counter[1]) 37 | 38 | 39 | plat = icebreaker.Platform() 40 | plat.add_extension([ 41 | ("triled", 0, Pins("PMOD1B:0")), 42 | ("triled", 1, Pins("PMOD1B:1")), 43 | ("triled", 2, Pins("PMOD1B:2")), 44 | ("triled", 3, Pins("PMOD1B:3")), 45 | ("triled", 4, Pins("PMOD1B:4")), 46 | ("triled", 5, Pins("PMOD1B:5")), 47 | ("triled", 6, Pins("PMOD1B:6")), 48 | ("triled", 7, Pins("PMOD1B:7")), 49 | ]) 50 | led = [plat.request("triled") for i in range(8)] 51 | my_blinker = Blinker(led, 10000000) 52 | plat.build(my_blinker) 53 | plat.create_programmer().flash(0, 'build/top.bin') 54 | -------------------------------------------------------------------------------- /tristate_fade/tristate_fade.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.build.generic_platform import * 3 | from migen.build.platforms import icebreaker 4 | 5 | class PWM(Module): 6 | def __init__(self, pwm, bitwidth, value): 7 | pwm_counter = Signal(bitwidth) 8 | self.sync += pwm.eq(pwm_counter < value) 9 | self.sync += pwm_counter.eq(pwm_counter + 1) 10 | 11 | class UpdownCounter(Module): 12 | def __init__(self, counter, bitwidth): 13 | icounter = Signal(bitwidth+1) 14 | direction = Signal() 15 | 16 | self.comb += direction.eq(icounter[bitwidth]) 17 | self.comb += If(direction, 18 | counter.eq(~icounter[0:bitwidth]) 19 | ).Else( 20 | counter.eq( icounter[0:bitwidth])) 21 | 22 | icounter_inv = Signal(bitwidth) 23 | self.comb += icounter_inv.eq(~icounter[0:bitwidth]) 24 | self.sync += If(icounter_inv == 0, 25 | icounter.eq(icounter + 2) 26 | ).Else( 27 | icounter.eq(icounter + 1)) 28 | 29 | class TickUpdownCounter(Module): 30 | def __init__(self, counter, tick, bitwidth): 31 | icounter = Signal(bitwidth+1) 32 | direction = Signal() 33 | 34 | self.comb += direction.eq(icounter[bitwidth]) 35 | self.comb += If(direction, 36 | counter.eq(~icounter[0:bitwidth]) 37 | ).Else( 38 | counter.eq( icounter[0:bitwidth])) 39 | 40 | icounter_inv = Signal(bitwidth) 41 | self.comb += icounter_inv.eq(~icounter[0:bitwidth]) 42 | self.sync += If(tick, 43 | If((icounter_inv) == 0, 44 | icounter.eq(icounter + 2) 45 | ).Else( 46 | icounter.eq(icounter + 1))) 47 | 48 | class ClockDiv(Module): 49 | def __init__(self, divbitwidth, divout, divtick): 50 | divcounter = Signal(divbitwidth+1) 51 | # count every clock tick 52 | self.sync += divcounter.eq(divcounter + 1) 53 | # output 50% duty cycle clock output 54 | self.comb += divout.eq(divcounter[divbitwidth]) 55 | # output a one clock wide strobe 56 | divcounter_inv = Signal(divbitwidth) 57 | self.comb += divcounter_inv.eq(~divcounter[0:divbitwidth]) 58 | self.comb += divtick.eq(divcounter_inv == 0) 59 | 60 | class TristatePins(Module): 61 | def __init__(self, n, pad, on, hiz): 62 | self.t = [] 63 | for i in range(n): 64 | self.t.append(TSTriple()) 65 | self.specials += self.t[i].get_tristate(pad[i]) 66 | self.comb += self.t[i].o.eq(on[i]) 67 | self.comb += self.t[i].oe.eq(~hiz[i]) 68 | 69 | class Fader(Module): 70 | def __init__(self, led): 71 | led_counter = Signal(3 + 1) 72 | pwm_counter_size = 11 73 | #pwm_counter_size = 3 74 | pwm_value_counter = Signal(pwm_counter_size) 75 | 76 | 77 | # divided clock for the updown counter 78 | updown_clk = Signal() 79 | updown_clk_strobe = Signal() 80 | self.submodules.updown_clk_div = \ 81 | ClockDiv(pwm_counter_size, updown_clk, updown_clk_strobe) 82 | 83 | # pwm_value_counter 84 | # counts up down to make a single led fade in and out 85 | self.submodules.pwm_value_updown = \ 86 | TickUpdownCounter(pwm_value_counter, updown_clk_strobe, pwm_counter_size) 87 | 88 | led_tick = Signal() 89 | prev_pwm_val_cnt = Signal(pwm_counter_size) 90 | self.sync += If((prev_pwm_val_cnt != pwm_value_counter) & 91 | (pwm_value_counter == 0), 92 | led_tick.eq(1), 93 | prev_pwm_val_cnt.eq(pwm_value_counter) 94 | ).Else( 95 | led_tick.eq(0), 96 | prev_pwm_val_cnt.eq(pwm_value_counter) 97 | ) 98 | 99 | # led counter: 100 | # Three top bits select which one of the led outputs is used 101 | # Fourth bit selects sign (high/low) 102 | self.submodules.tick_updown = \ 103 | TickUpdownCounter(led_counter, led_tick, 3 + 1) 104 | 105 | # PWM generator 106 | pwm = Signal() 107 | self.submodules.pwm = PWM(pwm, pwm_counter_size, pwm_value_counter) 108 | 109 | # LEDs 110 | on = [Signal() for l in led] 111 | hiz = [Signal() for l in led] 112 | self.submodules.tri = TristatePins(len(led), led, on, hiz) 113 | 114 | # Select high/low aka red green led 115 | for o in on: 116 | self.comb += o.eq(led_counter[0]) 117 | 118 | # Fade the specific led by PWMing the oe of the GPIO 119 | led_active = Signal(3) 120 | self.comb += led_active.eq(led_counter[1:4]) 121 | self.comb += hiz[0].eq(~(pwm & (led_active == 0))) 122 | self.comb += hiz[1].eq(~(pwm & (led_active == 1))) 123 | self.comb += hiz[2].eq(~(pwm & (led_active == 2))) 124 | self.comb += hiz[3].eq(~(pwm & (led_active == 3))) 125 | self.comb += hiz[4].eq(~(pwm & (led_active == 4))) 126 | self.comb += hiz[5].eq(~(pwm & (led_active == 5))) 127 | self.comb += hiz[6].eq(~(pwm & (led_active == 6))) 128 | self.comb += hiz[7].eq(~(pwm & (led_active == 7))) 129 | 130 | def _test(dut): 131 | for i in range(10000): 132 | yield 133 | 134 | sim = False 135 | if __name__ == "__main__": 136 | import sys 137 | if len(sys.argv) != 1: 138 | if sys.argv[1] == "sim": 139 | sim = True 140 | pwm_out = Signal() 141 | dbg_sig = Signal() 142 | led = Signal(8) 143 | dut = Fader(led) 144 | dut.clock_domains.cd_sys = ClockDomain("sys") 145 | run_simulation(dut, _test(dut), vcd_name="tristate_fade.vcd") 146 | else: 147 | plat = icebreaker.Platform() 148 | plat.add_extension([ 149 | ("triled", 0, Pins("PMOD1B:0")), 150 | ("triled", 1, Pins("PMOD1B:1")), 151 | ("triled", 2, Pins("PMOD1B:2")), 152 | ("triled", 3, Pins("PMOD1B:3")), 153 | ("triled", 4, Pins("PMOD1B:4")), 154 | ("triled", 5, Pins("PMOD1B:5")), 155 | ("triled", 6, Pins("PMOD1B:6")), 156 | ("triled", 7, Pins("PMOD1B:7")), 157 | ]) 158 | led = [plat.request("triled") for i in range(8)] 159 | my_blinker = Fader(led) 160 | plat.build(my_blinker) 161 | plat.create_programmer().flash(0, 'build/top.bin') 162 | -------------------------------------------------------------------------------- /uart/uart.py: -------------------------------------------------------------------------------- 1 | # This example is based on Whitequark's lab notebook 2 | # https://lab.whitequark.org/notes/2016-10-18/implementing-an-uart-in-verilog-and-migen/ 3 | 4 | from migen import * 5 | from migen.genlib.fsm import * 6 | 7 | 8 | def _divisor(freq_in, freq_out, max_ppm=None): 9 | divisor = freq_in // freq_out 10 | if divisor <= 0: 11 | raise ArgumentError("output frequency is too high") 12 | 13 | ppm = 1000000 * ((freq_in / divisor) - freq_out) / freq_out 14 | if max_ppm is not None and ppm > max_ppm: 15 | raise ArgumentError("output frequency deviation is too high") 16 | 17 | return divisor 18 | 19 | 20 | class UART(Module): 21 | def __init__(self, serial, clk_freq, baud_rate): 22 | self.rx_data = Signal(8) 23 | self.rx_ready = Signal() 24 | self.rx_ack = Signal() 25 | self.rx_error = Signal() 26 | 27 | self.tx_data = Signal(8) 28 | self.tx_ready = Signal() 29 | self.tx_ack = Signal() 30 | 31 | divisor = _divisor(freq_in=clk_freq, freq_out=baud_rate, max_ppm=50000) 32 | 33 | ### 34 | 35 | rx_counter = Signal(max=divisor) 36 | self.rx_strobe = rx_strobe = Signal() 37 | self.comb += rx_strobe.eq(rx_counter == 0) 38 | self.sync += \ 39 | If(rx_counter == 0, 40 | rx_counter.eq(divisor - 1) 41 | ).Else( 42 | rx_counter.eq(rx_counter - 1) 43 | ) 44 | 45 | self.rx_bitno = rx_bitno = Signal(3) 46 | self.submodules.rx_fsm = FSM(reset_state="IDLE") 47 | self.rx_fsm.act("IDLE", 48 | If(~serial.rx, 49 | NextValue(rx_counter, divisor // 2), 50 | NextState("START") 51 | ) 52 | ) 53 | self.rx_fsm.act("START", 54 | If(rx_strobe, 55 | NextState("DATA") 56 | ) 57 | ) 58 | self.rx_fsm.act("DATA", 59 | If(rx_strobe, 60 | NextValue(self.rx_data, Cat(self.rx_data[1:8], serial.rx)), 61 | NextValue(rx_bitno, rx_bitno + 1), 62 | If(rx_bitno == 7, 63 | NextState("STOP") 64 | ) 65 | ) 66 | ) 67 | self.rx_fsm.act("STOP", 68 | If(rx_strobe, 69 | If(~serial.rx, 70 | NextState("ERROR") 71 | ).Else( 72 | NextState("FULL") 73 | ) 74 | ) 75 | ) 76 | self.rx_fsm.act("FULL", 77 | self.rx_ready.eq(1), 78 | If(self.rx_ack, 79 | NextState("IDLE") 80 | ).Elif(~serial.rx, 81 | NextState("ERROR") 82 | ) 83 | ) 84 | self.rx_fsm.act("ERROR", 85 | self.rx_error.eq(1)) 86 | 87 | ### 88 | 89 | tx_counter = Signal(max=divisor) 90 | self.tx_strobe = tx_strobe = Signal() 91 | self.comb += tx_strobe.eq(tx_counter == 0) 92 | self.sync += \ 93 | If(tx_counter == 0, 94 | tx_counter.eq(divisor - 1) 95 | ).Else( 96 | tx_counter.eq(tx_counter - 1) 97 | ) 98 | 99 | self.tx_bitno = tx_bitno = Signal(3) 100 | self.tx_latch = tx_latch = Signal(8) 101 | self.submodules.tx_fsm = FSM(reset_state="IDLE") 102 | self.tx_fsm.act("IDLE", 103 | self.tx_ack.eq(1), 104 | If(self.tx_ready, 105 | NextValue(tx_counter, divisor - 1), 106 | NextValue(tx_latch, self.tx_data), 107 | NextState("START") 108 | ).Else( 109 | NextValue(serial.tx, 1) 110 | ) 111 | ) 112 | self.tx_fsm.act("START", 113 | If(self.tx_strobe, 114 | NextValue(serial.tx, 0), 115 | NextState("DATA") 116 | ) 117 | ) 118 | self.tx_fsm.act("DATA", 119 | If(self.tx_strobe, 120 | NextValue(serial.tx, tx_latch[0]), 121 | NextValue(tx_latch, Cat(tx_latch[1:8], 0)), 122 | NextValue(tx_bitno, tx_bitno + 1), 123 | If(self.tx_bitno == 7, 124 | NextState("STOP") 125 | ) 126 | ) 127 | ) 128 | self.tx_fsm.act("STOP", 129 | If(self.tx_strobe, 130 | NextValue(serial.tx, 1), 131 | NextState("IDLE") 132 | ) 133 | ) 134 | 135 | 136 | class _TestPads(Module): 137 | def __init__(self): 138 | self.rx = Signal(reset=1) 139 | self.tx = Signal() 140 | 141 | 142 | def _test_rx(rx, dut): 143 | def T(): 144 | yield; yield; yield; yield 145 | def B(bit): 146 | yield rx.eq(bit) 147 | yield from T() 148 | def S(): 149 | yield from B(0) 150 | assert (yield dut.rx_error) == 0 151 | assert (yield dut.rx_ready) == 0 152 | def D(bit): 153 | yield from B(bit) 154 | assert (yield dut.rx_error) == 0 155 | assert (yield dut.rx_ready) == 0 156 | def E(): 157 | yield from B(1) 158 | assert (yield dut.rx_error) == 0 159 | def O(bits): 160 | yield from S() 161 | for bit in bits: 162 | yield from D(bit) 163 | yield from E() 164 | 165 | def A(octet): 166 | yield from T() 167 | assert (yield dut.rx_data) == octet 168 | yield dut.rx_ack.eq(1) 169 | while (yield dut.rx_ready) == 1: yield 170 | yield dut.rx_ack.eq(0) 171 | def F(): 172 | yield from T() 173 | assert (yield dut.rx_error) == 1 174 | yield rx.eq(1) 175 | yield dut.cd_sys.rst.eq(1) 176 | yield 177 | yield 178 | yield dut.cd_sys.rst.eq(0) 179 | yield 180 | yield 181 | assert (yield dut.rx_error) == 0 182 | 183 | # bit patterns 184 | yield from O([1, 0, 1, 0, 1, 0, 1, 0]) 185 | yield from A(0x55) 186 | yield from O([1, 1, 0, 0, 0, 0, 1, 1]) 187 | yield from A(0xC3) 188 | yield from O([1, 0, 0, 0, 0, 0, 0, 1]) 189 | yield from A(0x81) 190 | yield from O([1, 0, 1, 0, 0, 1, 0, 1]) 191 | yield from A(0xA5) 192 | yield from O([1, 1, 1, 1, 1, 1, 1, 1]) 193 | yield from A(0xFF) 194 | 195 | # framing error 196 | yield from S() 197 | for bit in [1, 1, 1, 1, 1, 1, 1, 1]: 198 | yield from D(bit) 199 | yield from S() 200 | yield from F() 201 | 202 | # overflow error 203 | yield from O([1, 1, 1, 1, 1, 1, 1, 1]) 204 | yield from B(0) 205 | yield from F() 206 | 207 | 208 | def _test_tx(tx, dut): 209 | def Th(): 210 | yield; yield 211 | def T(): 212 | yield; yield; yield; yield 213 | def B(bit): 214 | yield from T() 215 | assert (yield tx) == bit 216 | def S(octet): 217 | assert (yield tx) == 1 218 | assert (yield dut.tx_ack) == 1 219 | yield dut.tx_data.eq(octet) 220 | yield dut.tx_ready.eq(1) 221 | while (yield tx) == 1: yield 222 | yield dut.tx_ready.eq(0) 223 | assert (yield tx) == 0 224 | assert (yield dut.tx_ack) == 0 225 | yield from Th() 226 | def D(bit): 227 | assert (yield dut.tx_ack) == 0 228 | yield from B(bit) 229 | def E(): 230 | assert (yield dut.tx_ack) == 0 231 | yield from B(1) 232 | yield from Th() 233 | def O(octet, bits): 234 | yield from S(octet) 235 | for bit in bits: 236 | yield from D(bit) 237 | yield from E() 238 | 239 | yield from O(0x55, [1, 0, 1, 0, 1, 0, 1, 0]) 240 | yield from O(0x81, [1, 0, 0, 0, 0, 0, 0, 1]) 241 | yield from O(0xFF, [1, 1, 1, 1, 1, 1, 1, 1]) 242 | yield from O(0x00, [0, 0, 0, 0, 0, 0, 0, 0]) 243 | 244 | 245 | def _test(tx, rx, dut): 246 | yield from _test_rx(rx, dut) 247 | yield from _test_tx(tx, dut) 248 | 249 | 250 | class _LoopbackTest(Module): 251 | def __init__(self, platform): 252 | serial = platform.request("serial") 253 | leds = Cat([platform.request("user_ledr_n"), platform.request("user_ledg_n")]) 254 | debug = platform.request("debug") 255 | 256 | self.submodules.uart = UART(serial, clk_freq=12000000, baud_rate=115200) 257 | 258 | empty = Signal(reset=1) 259 | data = Signal(8) 260 | rx_strobe = Signal() 261 | tx_strobe = Signal() 262 | self.comb += [ 263 | rx_strobe.eq(self.uart.rx_ready & empty), 264 | tx_strobe.eq(self.uart.tx_ack & ~empty), 265 | self.uart.rx_ack.eq(rx_strobe), 266 | self.uart.tx_data.eq(data), 267 | self.uart.tx_ready.eq(tx_strobe) 268 | ] 269 | self.sync += [ 270 | If(rx_strobe, 271 | data.eq(self.uart.rx_data), 272 | empty.eq(0) 273 | ), 274 | If(tx_strobe, 275 | empty.eq(1) 276 | ) 277 | ] 278 | 279 | self.comb += [ 280 | leds.eq(self.uart.rx_data[0:2]), 281 | debug.eq(Cat( 282 | serial.rx, 283 | serial.tx, 284 | self.uart.rx_strobe, 285 | self.uart.tx_strobe, 286 | # self.uart.rx_fsm.ongoing("IDLE"), 287 | # self.uart.rx_fsm.ongoing("START"), 288 | # self.uart.rx_fsm.ongoing("DATA"), 289 | # self.uart.rx_fsm.ongoing("STOP"), 290 | # self.uart.rx_fsm.ongoing("FULL"), 291 | # self.uart.rx_fsm.ongoing("ERROR"), 292 | # self.uart.tx_fsm.ongoing("IDLE"), 293 | # self.uart.tx_fsm.ongoing("START"), 294 | # self.uart.tx_fsm.ongoing("DATA"), 295 | # self.uart.tx_fsm.ongoing("STOP"), 296 | )) 297 | ] 298 | 299 | 300 | if __name__ == "__main__": 301 | import sys 302 | if sys.argv[1] == "sim": 303 | pads = _TestPads() 304 | dut = UART(pads, clk_freq=4800, baud_rate=1200) 305 | dut.clock_domains.cd_sys = ClockDomain("sys") 306 | run_simulation(dut, _test(pads.tx, pads.rx, dut), vcd_name="uart.vcd") 307 | elif sys.argv[1] == "loopback": 308 | from migen.build.generic_platform import * 309 | from migen.build.platforms import icebreaker 310 | 311 | plat = icebreaker.Platform() 312 | 313 | # The debug pins are on the PMOD1A in the following order on the connector: 314 | # 7 8 9 10 1 2 3 4 315 | # Yes that means that the pins at the edge of the board come first 316 | # and the pins further away from the edge second 317 | plat.add_extension([ 318 | ("debug", 0, Pins("PMOD1A:4")), 319 | ("debug", 1, Pins("PMOD1A:5")), 320 | ("debug", 2, Pins("PMOD1A:6")), 321 | ("debug", 3, Pins("PMOD1A:7")), 322 | ("debug", 4, Pins("PMOD1A:0")), 323 | ("debug", 5, Pins("PMOD1A:1")), 324 | ("debug", 6, Pins("PMOD1A:2")), 325 | ("debug", 7, Pins("PMOD1A:3")), 326 | ]) 327 | 328 | plat.build(_LoopbackTest(plat)) 329 | plat.create_programmer().flash(0, "build/top.bin") 330 | --------------------------------------------------------------------------------