diff options
Diffstat (limited to 'fpga')
-rw-r--r-- | fpga/src/Clock.bs | 19 | ||||
-rw-r--r-- | fpga/src/HyperBus.bs | 31 | ||||
-rw-r--r-- | fpga/src/I2C.bs | 3 | ||||
-rw-r--r-- | fpga/src/Numini.bs | 77 | ||||
-rw-r--r-- | fpga/src/Top.bs | 225 | ||||
-rw-r--r-- | fpga/src/Uart.bs | 65 |
6 files changed, 290 insertions, 130 deletions
diff --git a/fpga/src/Clock.bs b/fpga/src/Clock.bs new file mode 100644 index 0000000..c908502 --- /dev/null +++ b/fpga/src/Clock.bs @@ -0,0 +1,19 @@ +-- | A simple clock package. +package Clock where + +interface Clock = + clk :: Bool + +-- TODO: Make a multiclock that uses one count for all the sub-clocks in the +-- design. + +mkClock :: Bit n -> Module Clock +mkClock divisor = + module + count :: Reg (Bit n) <- mkReg 0 + rules + when True ==> count := if count == divisor - 1 then 0 else count + 1 + interface Clock + clk = count == 0 + +-- vim: set ft=haskell : diff --git a/fpga/src/HyperBus.bs b/fpga/src/HyperBus.bs new file mode 100644 index 0000000..41d1703 --- /dev/null +++ b/fpga/src/HyperBus.bs @@ -0,0 +1,31 @@ +-- | A controller for a HyperBus interface, allowing for I/O to four chips. +package HyperBus where + +import Clock + +-- | The HyperBus output pins. +interface HyperBusOut = + ck :: Bit 1 + ck_n :: Bit 1 + cs0_n :: Bit 1 + cs1_n :: Bit 1 + cs2_n :: Bit 1 + cs3_n :: Bit 1 + reset_n :: Bit 1 + rwds_out :: Maybe (Bit 1) + dq_out :: Maybe (Bit 8) + +mkHyperBus :: Wire (Bit 1) -> Wire (Bit 8) -> Clock -> Module HyperBusOut +mkHyperBus rwds_in dq_in clock = module + interface HyperBusOut + ck = if clock.clk then 1 else 0 + ck_n = if clock.clk then 0 else 1 + cs0_n = 1 + cs1_n = 1 + cs2_n = 1 + cs3_n = 1 + reset_n = 1 + rwds_out = Nothing + dq_out = Nothing + +-- vim: set ft=haskell : diff --git a/fpga/src/I2C.bs b/fpga/src/I2C.bs new file mode 100644 index 0000000..f3cd760 --- /dev/null +++ b/fpga/src/I2C.bs @@ -0,0 +1,3 @@ +package I2C where + +-- vim: set ft=haskell : diff --git a/fpga/src/Numini.bs b/fpga/src/Numini.bs new file mode 100644 index 0000000..58c2c38 --- /dev/null +++ b/fpga/src/Numini.bs @@ -0,0 +1,77 @@ +-- | The main board, set up to be wired to either the Inkplate's top-level or +-- the simulator's. This insulates us from Bluesim's lack of Inout support. +package Numini where + +import Clock +import HyperBus +import Uart + +-- | The output pins. +interface NuminiOut = + ch559_uart_tx :: Bit 1 + inkplate_uart_tx :: Bit 1 + usb_uart_tx :: Bit 1 + + led_r_n :: Bit 1 + led_g_n :: Bit 1 + + rgb_r_n :: Bit 1 + rgb_g_n :: Bit 1 + rgb_b_n :: Bit 1 + + hyperbus_ck :: Bit 1 + hyperbus_ck_n :: Bit 1 + hyperbus_cs0_n :: Bit 1 + hyperbus_cs1_n :: Bit 1 + hyperbus_cs2_n :: Bit 1 + hyperbus_cs3_n :: Bit 1 + hyperbus_reset_n :: Bit 1 + hyperbus_rwds_out :: Maybe (Bit 1) + hyperbus_dq_out :: Maybe (Bit 8) + + i2c_scl :: Bit 1 + i2c_sda_out :: Maybe (Bit 1) + +clockFreqHz :: Bit 24 +clockFreqHz = 12_000_000 + +mkNumini :: Wire (Bit 1) -> Wire (Bit 1) -> Wire (Bit 1) -> Wire (Bit 1) -> + Wire (Bit 8) -> Wire (Bit 1) -> Module NuminiOut +mkNumini ch559_uart_rx inkplate_uart_rx usb_uart_rx hyperbus_rwds_in + hyperbus_dq_in i2c_sda_in = module + clk9600 <- mkClock (clockFreqHz / 9600) + clk2M <- mkClock (clockFreqHz / 2_000_000) + clk3M <- mkClock (clockFreqHz / 3_000_000) + + ch559_uart <- mkUart ch559_uart_rx clk9600 + inkplate_uart <- mkUart inkplate_uart_rx clk2M + usb_uart <- mkUart usb_uart_rx clk9600 + + hyperbus <- mkHyperBus hyperbus_rwds_in hyperbus_dq_in clk3M + + interface NuminiOut + ch559_uart_tx = ch559_uart.tx + inkplate_uart_tx = inkplate_uart.tx + usb_uart_tx = usb_uart.tx + + led_r_n = 1 + led_g_n = 1 + + rgb_r_n = 1 + rgb_g_n = 1 + rgb_b_n = 1 + + hyperbus_ck = hyperbus.ck + hyperbus_ck_n = hyperbus.ck_n + hyperbus_cs0_n = hyperbus.cs0_n + hyperbus_cs1_n = hyperbus.cs1_n + hyperbus_cs2_n = hyperbus.cs2_n + hyperbus_cs3_n = hyperbus.cs3_n + hyperbus_reset_n = hyperbus.reset_n + hyperbus_rwds_out = hyperbus.rwds_out + hyperbus_dq_out = hyperbus.dq_out + + i2c_scl = 1 + i2c_sda_out = Nothing + +-- vim: set ft=haskell : diff --git a/fpga/src/Top.bs b/fpga/src/Top.bs index 29558d4..df79387 100644 --- a/fpga/src/Top.bs +++ b/fpga/src/Top.bs @@ -4,108 +4,165 @@ package Top where import Connectable import CPU import GetPut +import Numini import TriState import Uart -- | The interface to the iCEBreaker. interface Top = -- RS232 - rx :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [RX] #-} - tx :: Bit 1 {-# always_ready, result = TX #-} + usb_uart_rx :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [RX] #-} + usb_uart_tx :: Bit 1 {-# always_ready, result = TX #-} -- Onboard LEDs - ledR_N :: Bit 1 {-# always_ready, result = LEDR_N #-} - ledG_N :: Bit 1 {-# always_ready, result = LEDG_N #-} + led_r_n :: Bit 1 {-# always_ready, result = LEDR_N #-} + led_g_n :: Bit 1 {-# always_ready, result = LEDG_N #-} -- RGB LED driver - ledRed_N :: Bit 1 {-# always_ready, result = LED_RED_N #-} - ledGrn_N :: Bit 1 {-# always_ready, result = LED_GRN_N #-} - ledBlu_N :: Bit 1 {-# always_ready, result = LED_BLU_N #-} + rgb_r_n :: Bit 1 {-# always_ready, result = LED_RED_N #-} + rgb_g_n :: Bit 1 {-# always_ready, result = LED_GRN_N #-} + rgb_b_n :: Bit 1 {-# always_ready, result = LED_BLU_N #-} -- HyperBus 1 (PMOD 1A) - hyperBus_CS2_N :: Bit 1 {-# always_ready, result = P1A1 #-} - hyperBus_CS0_N :: Bit 1 {-# always_ready, result = P1A2 #-} - hyperBus_CK :: Bit 1 {-# always_ready, result = P1A3 #-} - hyperBus_CK_N :: Bit 1 {-# always_ready, result = P1A4 #-} - hyperBus_CS3_N :: Bit 1 {-# always_ready, result = P1A7 #-} - hyperBus_CS1_N :: Bit 1 {-# always_ready, result = P1A8 #-} - hyperBus_RESET_N :: Bit 1 {-# always_ready, result = P1A9 #-} - hyperBus_RWDS :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P1A10] #-} + hyperbus_cs2_n :: Bit 1 {-# always_ready, result = P1A1 #-} + hyperbus_cs0_n :: Bit 1 {-# always_ready, result = P1A2 #-} + hyperbus_ck :: Bit 1 {-# always_ready, result = P1A3 #-} + hyperbus_ck_n :: Bit 1 {-# always_ready, result = P1A4 #-} + hyperbus_cs3_n :: Bit 1 {-# always_ready, result = P1A7 #-} + hyperbus_cs1_n :: Bit 1 {-# always_ready, result = P1A8 #-} + hyperbus_reset_n :: Bit 1 {-# always_ready, result = P1A9 #-} + hyperbus_rwds :: Inout (Bit 1) {-# prefix = "P1A10" #-} -- HyperBus 2 (PMOD 1B) - hyperBus_DQ0 :: Inout (Bit 1) {-# prefix = "P1B1" #-} - hyperBus_DQ1 :: Inout (Bit 1) {-# prefix = "P1B2" #-} - hyperBus_DQ2 :: Inout (Bit 1) {-# prefix = "P1B3" #-} - hyperBus_DQ3 :: Inout (Bit 1) {-# prefix = "P1B4" #-} - hyperBus_DQ7 :: Inout (Bit 1) {-# prefix = "P1B7" #-} - hyperBus_DQ6 :: Inout (Bit 1) {-# prefix = "P1B8" #-} - hyperBus_DQ5 :: Inout (Bit 1) {-# prefix = "P1B9" #-} - hyperBus_DQ4 :: Inout (Bit 1) {-# prefix = "P1B10" #-} - -- LEDs and buttons (PMOD 2) - led1 :: Bit 1 {-# always_ready, result = P2_7 #-} - led2 :: Bit 1 {-# always_ready, result = P2_1 #-} - led3 :: Bit 1 {-# always_ready, result = P2_2 #-} - led4 :: Bit 1 {-# always_ready, result = P2_8 #-} - led5 :: Bit 1 {-# always_ready, result = P2_3 #-} - btn1 :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P2_9] #-} - btn2 :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P2_4] #-} - btn3 :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P2_10] #-} + hyperbus_dq0 :: Inout (Bit 1) {-# prefix = "P1B1" #-} + hyperbus_dq1 :: Inout (Bit 1) {-# prefix = "P1B2" #-} + hyperbus_dq2 :: Inout (Bit 1) {-# prefix = "P1B3" #-} + hyperbus_dq3 :: Inout (Bit 1) {-# prefix = "P1B4" #-} + hyperbus_dq7 :: Inout (Bit 1) {-# prefix = "P1B7" #-} + hyperbus_dq6 :: Inout (Bit 1) {-# prefix = "P1B8" #-} + hyperbus_dq5 :: Inout (Bit 1) {-# prefix = "P1B9" #-} + hyperbus_dq4 :: Inout (Bit 1) {-# prefix = "P1B10" #-} + -- Serial buses (PMOD 2) + ch559_uart_rx :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P2_1] #-} + ch559_uart_tx :: Bit 1 {-# always_ready, result = P2_2 #-} + inkplate_uart_rx :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P2_3] #-} + inkplate_uart_tx :: Bit 1 {-# always_ready, result = P2_4 #-} + i2c_scl :: Bit 1 {-# always_ready, result = P2_7 #-} + i2c_sda :: Inout (Bit 1) {-# prefix = "P2_8" #-} + todo_btn :: Bit 1 -> Action {-# always_enabled, always_ready, prefix = "", arg_names = [P2_9] #-} + todo_led :: Bit 1 {-# always_ready, result = P2_10 #-} clockFreqHz :: Integer clockFreqHz = 12_000_000 mkTop :: Module Top -mkTop = - module - cpu <- mkCPU +mkTop = module + ch559_uart_rx <- mkWire + inkplate_uart_rx <- mkWire + usb_uart_rx <- mkWire - uart <- mkUart (clockFreqHz / 9600) - mkConnection cpu.uart_tx uart.send - mkConnection cpu.uart_rx uart.recv + -- Make tristates for the HyperBus inouts. + hyperbus_rwds_enable :: Reg Bool <- mkReg False + hyperbus_rwds_out :: Reg (Bit 1) <- mkReg 0 + hyperbus_dq_enable :: Reg Bool <- mkReg False + hyperbus_dq_out :: Reg (Bit 8) <- mkReg 0 + hyperbus_rwds <- mkTriState hyperbus_rwds_enable hyperbus_rwds_out + hyperbus_dq0 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[0:0] + hyperbus_dq1 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[1:1] + hyperbus_dq2 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[2:2] + hyperbus_dq3 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[3:3] + hyperbus_dq4 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[4:4] + hyperbus_dq5 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[5:5] + hyperbus_dq6 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[6:6] + hyperbus_dq7 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[7:7] - hyperBus_data0 <- mkTriState True 1 - hyperBus_data1 <- mkTriState True 1 - hyperBus_data2 <- mkTriState True 1 - hyperBus_data3 <- mkTriState True 1 - hyperBus_data4 <- mkTriState True 1 - hyperBus_data5 <- mkTriState True 1 - hyperBus_data6 <- mkTriState True 1 - hyperBus_data7 <- mkTriState True 1 + -- Make a tristate for the I2C inout. + i2c_sda_enable <- mkReg False + i2c_sda_out <- mkReg 0 + i2c_sda <- mkTriState i2c_sda_enable i2c_sda_out - interface Top - -- RS232 - rx = uart.rxPin - tx = uart.txPin - -- Onboard LEDs - ledR_N = uart.txPin - ledG_N = 1 - -- RGB LED driver - ledRed_N = 1 - ledGrn_N = 1 - ledBlu_N = 1 - -- HyperBus 1 (PMOD 1A) - hyperBus_CS2_N = 1 - hyperBus_CS0_N = 1 - hyperBus_CK = 0 - hyperBus_CK_N = 1 - hyperBus_CS3_N = 1 - hyperBus_CS1_N = 1 - hyperBus_RESET_N = 1 - hyperBus_RWDS _ = noAction - -- HyperBus 2 (PMOD 1B) - hyperBus_DQ0 = hyperBus_data0.io - hyperBus_DQ1 = hyperBus_data1.io - hyperBus_DQ2 = hyperBus_data2.io - hyperBus_DQ3 = hyperBus_data3.io - hyperBus_DQ4 = hyperBus_data4.io - hyperBus_DQ5 = hyperBus_data5.io - hyperBus_DQ6 = hyperBus_data6.io - hyperBus_DQ7 = hyperBus_data7.io - -- LEDs and buttons (PMOD 2) - led1 = 0 - led2 = 0 - led3 = 0 - led4 = 0 - led5 = 0 - btn1 _ = noAction - btn2 _ = noAction - btn3 _ = noAction + -- Make wires for all the inout inputs. + hyperbus_rwds_in :: Wire (Bit 1) <- mkWire + hyperbus_dq_in :: Wire (Bit 8) <- mkWire + i2c_sda_in :: Wire (Bit 1) <- mkWire + + numini <- mkNumini ch559_uart_rx inkplate_uart_rx usb_uart_rx + hyperbus_rwds_in hyperbus_dq_in i2c_sda_in + + -- Wire up the tristates. + rules + "update_hyperbus_rwds_out": when True ==> do + case numini.hyperbus_rwds_out of + Just bits -> do + hyperbus_rwds_enable := True + hyperbus_rwds_out := bits + Nothing -> do + hyperbus_rwds_enable := False + hyperbus_rwds_out := 0 + "update_hyperbus_rwds_in": when True ==> do + hyperbus_rwds_in := hyperbus_rwds._read + "update_hyperbus_dq_out": when True ==> do + case numini.hyperbus_dq_out of + Just bits -> do + hyperbus_dq_enable := True + hyperbus_dq_out := bits + Nothing -> do + hyperbus_dq_enable := False + hyperbus_dq_out := 0 + "update_hyperbus_dq_in": when True ==> do + hyperbus_dq_in := hyperbus_dq7._read + ++ hyperbus_dq6._read + ++ hyperbus_dq5._read + ++ hyperbus_dq4._read + ++ hyperbus_dq3._read + ++ hyperbus_dq2._read + ++ hyperbus_dq1._read + ++ hyperbus_dq0._read + "update_i2c_sda_out": when True ==> do + case numini.i2c_sda_out of + Just bits -> do + i2c_sda_enable := True + i2c_sda_out := bits + Nothing -> do + i2c_sda_enable := False + i2c_sda_out := 0 + "update_i2c_sda_in": when True ==> do + i2c_sda_in := i2c_sda._read + + interface Top + -- RS232 + usb_uart_rx bit = usb_uart_rx := bit + usb_uart_tx = numini.usb_uart_tx + -- Onboard LEDs + led_r_n = numini.led_r_n + led_g_n = numini.led_g_n + -- RGB LED driver + rgb_r_n = numini.rgb_r_n + rgb_g_n = numini.rgb_g_n + rgb_b_n = numini.rgb_b_n + -- HyperBus 1 (PMOD 1A) + hyperbus_cs2_n = numini.hyperbus_cs2_n + hyperbus_cs0_n = numini.hyperbus_cs0_n + hyperbus_ck = numini.hyperbus_ck + hyperbus_ck_n = numini.hyperbus_ck_n + hyperbus_cs3_n = numini.hyperbus_cs3_n + hyperbus_cs1_n = numini.hyperbus_cs1_n + hyperbus_reset_n = numini.hyperbus_reset_n + hyperbus_rwds = hyperbus_rwds.io + -- HyperBus 2 (PMOD 1B) + hyperbus_dq0 = hyperbus_dq0.io + hyperbus_dq1 = hyperbus_dq1.io + hyperbus_dq2 = hyperbus_dq2.io + hyperbus_dq3 = hyperbus_dq3.io + hyperbus_dq4 = hyperbus_dq4.io + hyperbus_dq5 = hyperbus_dq5.io + hyperbus_dq6 = hyperbus_dq6.io + hyperbus_dq7 = hyperbus_dq7.io + -- LEDs and buttons (PMOD 2) + ch559_uart_rx bit = ch559_uart_rx := bit + ch559_uart_tx = numini.ch559_uart_tx + inkplate_uart_rx bit = inkplate_uart_rx := bit + inkplate_uart_tx = numini.inkplate_uart_tx + i2c_scl = numini.i2c_scl + i2c_sda = i2c_sda.io + todo_btn _ = noAction + todo_led = 0 {-# verilog mkTop #-} {-# properties mkTop = { RSTN = BTN_N } #-} diff --git a/fpga/src/Uart.bs b/fpga/src/Uart.bs index b54cfb8..7298083 100644 --- a/fpga/src/Uart.bs +++ b/fpga/src/Uart.bs @@ -1,26 +1,9 @@ package Uart where +import Clock import FIFOF import GetPut -interface Clock = - clk :: Bool - -mkDivider :: Integer -> Module Clock -mkDivider divisor = - module - count :: Reg (Bit 32) <- mkReg 0 - - rules - "increment_divider": when True ==> do - if count == fromInteger (divisor - 1) then do - count := 0 - else - count := count + 1 - - interface Clock - clk = count == 0 - -- | The state of the TX side of the UART. data TxState = -- | The UART is not currently sending anything. May transition to @@ -44,14 +27,14 @@ interface TxUart = send :: Put (Bit 8) mkTxUart :: Clock -> Integer -> Module TxUart -mkTxUart baudClock bufferSize = +mkTxUart clock bufferSize = module fifo :: FIFOF (Bit 8) <- mkSizedFIFOF bufferSize state :: Reg TxState <- mkReg Idle pin :: Reg (Bit 1) <- mkReg 1 rules - "uart_tx": when baudClock.clk + "uart_tx": when clock.clk rules "uart_tx_idle": when Idle <- state, not fifo.notEmpty ==> do pin := 1 @@ -90,58 +73,48 @@ data RxState -- | The RX side of the UART. interface RxUart = - -- | The RX pin. - pin :: Bit 1 -> Action -- | Reads a byte from the UART's receive buffer. recv :: Get (Bit 8) -mkRxUart :: Clock -> Integer -> Module RxUart -mkRxUart baudClock bufferSize = +mkRxUart :: Wire (Bit 1) -> Clock -> Integer -> Module RxUart +mkRxUart rx clock bufferSize = module fifo :: FIFOF (Bit 8) <- mkGSizedFIFOF True False bufferSize state :: Reg RxState <- mkReg Idle - pin :: Wire (Bit 1) <- mkWire rules - "uart_rx": when baudClock.clk + "uart_rx": when clock.clk rules - "uart_rx_idle_to_start": when Idle <- state, pin == 0 ==> do + "uart_rx_idle_to_start": when Idle <- state, rx == 0 ==> do state := Data 0 0 "uart_rx_data_to_data": when Data bits n <- state, n < 7 ==> do - state := Data (pin ++ bits[7:1]) (n + 1) + state := Data (rx ++ bits[7:1]) (n + 1) "uart_rx_data_to_stop": when Data bits 7 <- state ==> do - state := Stop (pin ++ bits[7:1]) - "uart_rx_stop_to_idle": when Stop bits <- state, pin == 1 ==> do + state := Stop (rx ++ bits[7:1]) + "uart_rx_stop_to_idle": when Stop bits <- state, rx == 1 ==> do fifo.enq bits state := Idle interface RxUart - pin bit = pin := bit recv = toGet fifo -- | An 8n1 UART. interface Uart = - -- | The RX pin. - rxPin :: Bit 1 -> Action -- | The TX pin. - txPin :: Bit 1 + tx :: Bit 1 -- | Reads a byte from the UART's receive buffer. recv :: Get (Bit 8) -- | Writes a byte to the UART's transmit buffer. send :: Put (Bit 8) -mkUart :: Integer -> Module Uart -mkUart baudDivisor = - module - baudClock <- mkDivider baudDivisor - rx <- mkRxUart baudClock 8 - tx <- mkTxUart baudClock 8 - - interface Uart - rxPin = rx.pin - txPin = tx.pin - recv = rx.recv - send = tx.send +mkUart :: Wire (Bit 1) -> Clock -> Module Uart +mkUart rx clock = module + uart_rx <- mkRxUart rx clock 8 + uart_tx <- mkTxUart clock 8 + interface Uart + tx = uart_tx.pin + recv = uart_rx.recv + send = uart_tx.send -- vim: set ft=haskell : |