diff --git a/lion-soc/README.md b/lion-soc/README.md index 53f718a..8954c2d 100644 --- a/lion-soc/README.md +++ b/lion-soc/README.md @@ -4,13 +4,13 @@ ## Setup * [Project IceStorm](https://github.com/standardsemiconductor/VELDT-info#project-icestorm) -* [riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain) +* [riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) * Need `riscv64-unknown-*` binaries e.g.: ```console - foo@bar:~$ git clone https://github.com/riscv/riscv-gnu-toolchain.git + foo@bar:~$ git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git foo@bar:~$ cd riscv-gnu-toolchain foo@bar:~/riscv-gnu-toolchain$ git submodule update --init --recursive - foo@bar:~/riscv-gnu-toolchain$ ./configure --prefix=/opt/riscv/ + foo@bar:~/riscv-gnu-toolchain$ ./configure --prefix=/opt/riscv/ --enable-multilib foo@bar:~/rsicv-gnu-toolchain$ sudo make foo@bar:~/riscv-gnu-toolchain$ export PATH=$PATH:/opt/riscv/bin ``` @@ -82,7 +82,7 @@ Status Byte: 76543210 ......** ||__Transmitter Status: 0 = Empty (Idle), 1 = Full (Busy) - |___Receiver Status: 0 = Empty, 1 = Full + |___Receiver Status: 0 = Full, 1 = Empty ``` #### UART Usage Examples @@ -101,7 +101,7 @@ Status Byte: li a0, 0x4 # set pointer to UART peripheral memory location 1: lbu a1, 0x2(a0) # read status register andi a1, a1, 0x2 # mask receiver status - beqz a1, 1b # wait until receiver full + bnez a1, 1b # wait until receiver full lbu a1, 0x1(a0) # read receiver buffer ``` ### SPI Flash diff --git a/lion-soc/app/Boot.hs b/lion-soc/app/Boot.hs new file mode 100644 index 0000000..755e3c2 --- /dev/null +++ b/lion-soc/app/Boot.hs @@ -0,0 +1,149 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +import Control.Applicative ((<|>)) +import Control.Concurrent (threadDelay) +import Control.Concurrent.Async ( + Concurrently(Concurrently, runConcurrently), + concurrently_, + mapConcurrently_, + race_ + ) +import Control.Concurrent.STM ( + TChan, + atomically, + dupTChan, + newBroadcastTChanIO, + newTChan, + newTChanIO, + readTChan, + writeTChan + ) +import Control.Monad (forever, forM_, join, void, (<=<)) +import qualified Data.ByteString.Char8 as C +import qualified Data.ByteString.Lazy.Char8 as LC ( + ByteString, + getContents, + hGetContents, + hPut, + pack, + putStr, + putStrLn, + readFile, + singleton, + takeWhile + ) +import System.Environment (getArgs) +import System.Hardware.Serialport ( + CommSpeed(CS19200), + SerialPortSettings(..), + defaultSerialSettings, + hWithSerial + ) +import System.IO ( + BufferMode(LineBuffering, NoBuffering), + Handle, + hGetChar, + hPutChar, + hSetBuffering, + hSetNewlineMode, + stdin, + universalNewlineMode + ) + +main :: IO () +main = boot =<< parseArgs <$> getArgs + +parseArgs :: [String] -> (FilePath, Maybe FilePath) +parseArgs = \case + [serialPath] -> (serialPath, Nothing) + [serialPath, imagePath] -> (serialPath, Just imagePath) + _ -> ("/dev/ttyUSB0", Just "_build/demo/led/led.bin") + +boot :: (FilePath, Maybe FilePath) -> IO () +boot (serialPath, imagePathM) = + hWithSerial serialPath serialSettings $ \serialHandle -> do + hSetBuffering stdin NoBuffering + hSetBuffering serialHandle NoBuffering + hSetNewlineMode serialHandle universalNewlineMode + interactSerial imagePathM =<< mkBoot serialHandle + +data Boot = Boot + { bootHandle :: Handle + , fromSerialTChan :: TChan LC.ByteString + , toSerialTChan :: TChan LC.ByteString + } + +mkBoot :: Handle -> IO Boot +mkBoot hndl = do + fromSerial <- newBroadcastTChanIO + toSerial <- newTChanIO + return $ Boot{ bootHandle = hndl + , fromSerialTChan = fromSerial + , toSerialTChan = toSerial + } + +interactSerial :: Maybe FilePath -> Boot -> IO () +interactSerial imagePathM boot = + mapConcurrently_ + ($ boot) + [ fromSerial + , toSerial + , toStdout + , setup imagePathM + ] + +fromSerial :: Boot -> IO () +fromSerial boot = do + bs <- LC.hGetContents $ bootHandle boot + atomically $ writeTChan (fromSerialTChan boot) bs + +toSerial :: Boot -> IO () +toSerial boot = join $ atomically $ do + bs <- readTChan $ toSerialTChan boot + return $ LC.hPut (bootHandle boot) bs + +fromStdin :: Boot -> IO () +fromStdin boot = writeSerial boot =<< LC.getContents + +toStdout :: Boot -> IO () +toStdout boot = LC.putStr =<< readSerial boot +toStdout boot = join $ atomically $ do + bs <- readTChan (fromSerialTChan boot) + return $ LC.putStr bs + +setup :: Maybe FilePath -> Boot -> IO () +setup imagePathM boot = do + write "a" + --atomically $ do + -- chan <- dupTChan $ fromSerialTChan boot + -- _ <- LC.takeWhile (/= '?') <$> readTChan chan + -- return () + --fmap (LC.dropWhile (/= '?')) $ readTChan =<< dupTChan (fromSerialTChan boot) + case imagePathM of + Nothing -> write "e" + Just path -> do + write "n" + LC.putStrLn $ "\nUploading " <> LC.pack path + write =<< LC.readFile path + LC.putStrLn "Done" + fromStdin boot + where + write = writeSerial boot + +writeSerial :: Boot -> LC.ByteString -> IO () +writeSerial boot = atomically . writeTChan (toSerialTChan boot) + +-- hPutChar serialHandle '\n' +-- threadDelay 1000 +-- hPutChar serialHandle 'n' +-- forM_ imagePathM $ C.hPut serialHandle <=< C.readFile +-- case imagePathM of +-- Nothing -> hPutChar serialHandle 'e' +-- Just imagePath -> do +-- hPutChar serialHandle 'n' +-- C.hPut serialHandle =<< C.readFile imagePath +-- interactUart serialHandle + +serialSettings :: SerialPortSettings +serialSettings = defaultSerialSettings{ commSpeed = CS19200 } diff --git a/lion-soc/app/Main.hs b/lion-soc/app/Main.hs index b86b17c..476764e 100644 --- a/lion-soc/app/Main.hs +++ b/lion-soc/app/Main.hs @@ -50,6 +50,9 @@ main = shakeArgs opts $ do , "_build/bios/bios.rom3" ] + phony "demo" $ do + need [ "_build/demo/led/led.bin"] + -- yosys synthesis "_build/Soc.json" %> \out -> do putInfo "Synthesizing Soc" @@ -111,6 +114,39 @@ main = shakeArgs opts $ do "bios/bios.S" "-o" [out] + + "_build/demo/led/led.o" %> \out -> do + need [ "demo/link.ld" + , "demo/crt0.S" + , "demo/firmware.c" + , "demo/led/led.c" + ] + cmd_ "riscv64-unknown-elf-gcc" + "-march=rv32i" + "-mabi=ilp32" + "-Wall" + "-g" + "-ffreestanding" + "-O0" + "-Wl,--gc-sections" + "-nostartfiles" + "-Wl,-T,demo/link.ld demo/crt0.S demo/firmware.c demo/led/led.c" + "-o" + [out] + "_build/demo/led/led.bin" %> \out -> do + need ["_build/demo/led/led.o"] + cmd_ "riscv64-unknown-elf-objcopy" + "-O" + "binary" + "_build/demo/led/led.o" + [out] + cmd_ "dd" + "if=/dev/zero" + "of=_build/demo/led/led.bin" + "bs=1" + "count=1" + "seek=131071" + where opts = shakeOptions { shakeFiles = buildDir diff --git a/lion-soc/bios/bios.S b/lion-soc/bios/bios.S index 701cd90..402cc31 100644 --- a/lion-soc/bios/bios.S +++ b/lion-soc/bios/bios.S @@ -5,22 +5,28 @@ .equ UART_BASE , 0x4 .equ SPI_BASE , 0x8 - .equ SPI_CR1 , 0x00000900 - .equ SPI_CR2 , 0x00000A00 - .equ SPI_BR , 0x00000B00 - .equ SPI_TXDR , 0x00000D00 +# .equ SPI_CR1 , 0x00000900 +# .equ SPI_CR2 , 0x00000A00 +# .equ SPI_BR , 0x00000B00 +# .equ SPI_TXDR , 0x00000D00 .equ SPI_RXDR , 0x00000E00 - .equ SPI_CSR , 0x00000F00 +# .equ SPI_CSR , 0x00000F00 .equ SPI_SR , 0x00000C00 .equ SPRAM_BASE, 0x20000 + .equ SPRAM_END, 0x40000 .section .text .globl _start _start: # set LED red - jal set_led_red + li a1, LED_BASE # set pointer LED_BASE + li a0, 0x0880 # byte1: cr0 address, byte0: enable bit + sh a0, (a1) # send command + + li a0, 0x01FF # byte1: pwrr address, byte0: full on + sh a0, (a1) # send command # wait until user presses any key jal get_char @@ -31,90 +37,117 @@ _start: la a0, copyright jal put_str - # check SPRAM -# la a0, check_spram_str -# jal put_str -# jal check_spram -# beqz a0, 1f -# la a0, fail_str -# j 2f -#1: la a0, success_str -#2: jal put_str - - # check SPI Flash - la a0, check_flash_str - jal put_str - jal init_spi - - # resume from deep power down - jal transfer_start_spi - li a0, 0xAB - jal transfer_spi - jal transfer_end_spi - - # read JEDEC ID - jal transfer_start_spi - - li a0, 0x9F - jal transfer_spi - - li a0, 0x00 - jal transfer_spi - mv t0, a0 + # choose (e)xisting, or upload (n)ew image? +1: la a0, image_prompt # load image prompt + jal put_str # print image prompt + jal get_char # read response + li a1, 0x65 # ascii e + li a2, 0x6e # ascii n + beq a0, a1, 7f # branch to use existing image + beq a0, a2, 2f # branch to upload new image + j 1b # invalid answer ask again. + + # upload new image from uart into ram +2: li t0, SPRAM_BASE # load SPRAM base + li t1, SPRAM_END # load SPRAM end +3: jal get_char # read byte + sb a0, (t0) # store byte + addi t0, t0, 1 # increment SPRAM address + bne t0, t1, 3b # if SPRAM not full, get another byte + + # check + #li a1, LED_BASE + #li a0, 0x0100 + #sh a0, (a1) + + # load ram into spi flash + li a0, SPI_BASE # initialize spi, set SPI_BASE pointer - li a0, 0x00 - jal transfer_spi - mv t1, a0 - - li a0, 0x00 - jal transfer_spi - mv t2, a0 - - jal transfer_end_spi - - # shift jedec out of t0,1,2 into a0 - li a0, 0x0 - slli t0, t0, 16 - or a0, a0, t0 - slli t1, t1, 8 - or a0, a0, t1 - or a0, a0, t2 - - # compare jedec vs expected then print success or failure. - li a1, 0x1F8501 # expected JEDEC ID - beq a0, a1, 3f # branch if equal - la a0, fail_str - j 4f -3: la a0, success_str -4: jal put_str - - # echo user input -5: jal get_char - jal put_char - j 5b - -init_spi: - li a0, SPI_BASE # set SPI_BASE pointer - # enable spi -1: lbu a1, 3(a0) # read bus status - bnez a1, 1b # wait until bus idle - li a1, 0x00010000 # set WRITE mode - li a2, SPI_CR1 # set CR1 address - or a1, a1, a2 # set CR1 address - ori a1, a1, 0x80 # set data +4: lbu a1, 3(a0) # read bus status + bnez a1, 4b # wait until bus idle + li a1, 0x00010980 # set WRITE mode, CR1 address, data sw a1, (a0) # send command + # check + #li a1, LED_BASE + #li a0, 0x0100 + #sh a0, (a1) + # set clock div -2: lbu a1, 3(a0) # read bus status - bnez a1, 2b # wait until bus idle - li a1, 0x00010000 # set WRITE mode - li a2, SPI_BR # set BR address - or a1, a1, a2 # set BR address - ori a1, a1, 0x03 # set data +5: lbu a1, 3(a0) # read bus status + bnez a1, 5b # wait until bus idle + li a1, 0x00010B03 # set WRITE mode, BR address, data sw a1, (a0) # send command - ret + # check + li a1, LED_BASE + li a0, 0x0100 + sh a0, (a1) + + jal transfer_start_spi # resume spi from deep power down + li a0, 0xAB # load command + jal transfer_spi # send command + jal transfer_end_spi # end + + jal write_enable # chip erase + jal transfer_start_spi # start transfer + li a0, 0x60 # load chip erase opcode + jal transfer_spi # send command + jal transfer_end_spi # end + + li t0, 0 # SPI flash address + li t1, SPRAM_BASE # SPRAM base address + li t2, SPRAM_END # SPRAM end address + +6: jal wait_spi_busy # wait while spi busy + + jal write_enable # byte program + jal transfer_start_spi # start transfer + li a0, 0x02 # load byte program opcode + jal transfer_spi # transfer byte + srli a0, t0, 16 # load 2 byte of SPI flash address + jal transfer_spi # send 2 byte of SPI flash address + srli a0, t0, 8 # load 1 byte of SPI flash address + jal transfer_spi # send 1 byte of SPI flash address + mv a0, t0 # load 0 byte of SPI flash address + jal transfer_spi # send 0 byte of SPI flash address + lbu a0, (t1) # load data byte from SPRAM + jal transfer_spi # send data byte from SPRAM + jal transfer_end_spi # end transfer + jal wait_spi_busy # wait while spi busy + addi t0, t0, 1 # increment SPI flash byte address + addi t1, t1, 1 # increment SPRAM byte address + bne t1, t2, 6b # transfer another byte while SPI flash has space + j 9f # jump to boot + + # load image from spi flash into ram +7: li t0, 0 # load SPI flash address + li t1, SPRAM_BASE # load SPRAM base address + li t2, SPRAM_END # load SPRAM end address + +8: jal transfer_start_spi # start SPI flash transfer + li a0, 0x03 # load read array opcode + jal transfer_spi # send read array opcode + srli a0, t0, 16 # load 2 byte of SPI flash address + jal transfer_spi # send 2 byte of SPI flash address + srli a0, t0, 8 # load 1 byte of SPI flash address + jal transfer_spi # send 1 byte of SPI flash address + mv a0, t0 # load 0 byte of SPI flash address + jal transfer_spi # send 0 byte of SPI flash address + li a0, 0x00 # load dummy byte + jal transfer_spi # send dummy byte, recv data byte + sb a0, (t1) # store data byte in SPRAM address + jal transfer_end_spi # end transfer + addi t0, t0, 1 # increment SPI flash byte address + addi t1, t1, 1 # increment SPRAM byte address + bne t1, t2, 8b # read another byte while SPRAM has space + + # boot from ram +9: #li a1, LED_BASE # set pointer LED_BASE + #li a0, 0x0100 # byte1: pwrr addr, byte0: full off + #sh a0, (a1) # send command + j SPRAM_BASE # boot image transfer_start_spi: @@ -123,18 +156,13 @@ transfer_start_spi: # set CSN 1: lbu a1, 3(a0) # read bus status bnez a1, 1b # wait until bus idle - li a1, 0x00010000 # set WRITE mode - li a2, SPI_CSR # set CSR address - or a1, a1, a2 # set CSR address + li a1, 0x00010F00 # set WRITE mode and CSR address sw a1, (a0) # send command # set spi master, cs hold 2: lbu a1, 3(a0) # read bus status bnez a1, 2b # wait until bus idle - li a1, 0x00010000 # set WRITE mode - li a2, SPI_CR2 # set CR2 address - or a1, a1, a2 # set CR2 address - ori a1, a1, 0xC0 # set data + li a1, 0x00010AC0 # set WRITE mode, CR2 address, data sw a1, (a0) # send command # wait trdy @@ -159,9 +187,7 @@ transfer_spi: # write txdr 1: lbu a2, 3(a1) # read bus status bnez a2, 1b # wait until bus idle - li a2, 0x00010000 # set WRITE mode - li a3, SPI_TXDR # set TXDR address - or a2, a2, a3 # set TXDR address + li a2, 0x00010D00 # set WRITE mode, TXDR address or a2, a2, a0 # set data sw a2, (a1) # send command @@ -194,27 +220,19 @@ transfer_end_spi: # set csn 1: lbu a1, 3(a0) # read bus status bnez a1, 1b # wait until bus idle - li a1, 0x00010000 # set WRITE mode - li a2, SPI_CSR # set CSR address - or a1, a1, a2 # set CSR address - ori a1, a1, 0x0F # set data + li a1, 0x00010F0F # set WRITE mode, CSR address, data sw a1, (a0) # send command # unset cs hold 2: lbu a1, 3(a0) # read bus status bnez a1, 2b # wait until bus idle - li a1, 0x00010000 # set WRITE mode - li a2, SPI_CR2 # set CR2 address - or a1, a1, a2 # set CR2 address - ori a1, a1, 0x80 # set data + li a1, 0x00010A80 # set WRITE mode, CR2 address, data sw a1, (a0) # send command # wait not tip 3: lbu a1, 3(a0) # read bus status bnez a1, 3b # wait until bus idle - li a1, 0x00000000 # set READ mode - li a2, SPI_SR # set SR address - or a1, a1, a2 # set SR address + li a1, 0x00000C00 # set READ mode, SR address sw a1, (a0) # send command 4: lbu a1, 3(a0) # read bus status bnez a1, 4b # wait until bus idle @@ -225,78 +243,41 @@ transfer_end_spi: ret -#check_spram: -# li a0, 0x0 -# li a1, SPRAM_BASE -# li a2, 0x1234abcd -# -# # check word -# sw a2, (a1) -# li a3, 0x0 # zero a3 -# lw a3, (a1) -# xor a3, a3, a2 -# or a0, a0, a3 -# -# # check half-word -# sh a2, 4(a1) # store lower half -# lhu a3, 4(a1) # load lower half -# li a4, 0xabcd -# xor a4, a4, a3 -# or a0, a0, a4 -# -# sh a2, 6(a1) # store to upper half -# lhu a3, 6(a1) # load from upper half -# li a4, 0xabcd -# xor a4, a4, a3 -# or a0, a0, a4 -# -# # check byte -# sb a2, 8(a1) -# lbu a3, 8(a1) -# li a4, 0xcd -# xor a4, a4, a3 -# or a0, a0, a4 -# -# sb a2, 9(a1) -# lbu a3, 9(a1) -# li a4, 0xcd -# xor a4, a4, a3 -# or a0, a0, a4 -# -# sb a2, 10(a1) -# lbu a3, 10(a1) -# li a4, 0xcd -# xor a4, a4, a3 -# or a0, a0, a4 -# -# sb a2, 11(a1) -# lbu a3, 11(a1) -# li a4, 0xcd -# xor a4, a4, a3 -# or a0, a0, a4 -# -# ret +write_enable: + mv s0, ra # save return address + jal transfer_start_spi # start SPI flash transfer + li a0, 0x06 # load write enable opcode + jal transfer_spi # send write enable opcode + jal transfer_end_spi # end transfer + mv ra, s0 # restore return address + ret + + +wait_spi_busy: + mv s0, ra # save return address +1: jal transfer_start_spi # start transfer + li a0, 0x05 # load read status register byte 1 opcode + jal transfer_spi # send read status command + li a0, 0x00 # load dummy byte + jal transfer_spi # read status byte + mv a2, a0 # move status byte to a2 register + jal transfer_end_spi # end transfer + andi a2, a2, 0x01 # mask busy status bit + bnez a2, 1b # 0 = device ready, 1 = device busy: continue poll + mv ra, s0 # restore return address + ret get_char: li a0, UART_BASE # set UART_BASE pointer 1: lbu a1, 0x2(a0) # read status register andi a1, a1, 0x2 # mask receiver status - beqz a1, 1b # wait until receiver full + bnez a1, 1b # wait until receiver full lbu a1, 0x1(a0) # read receiver buffer mv a0, a1 # return data in a0 ret -put_char: - li a1, UART_BASE # set UART_BASE pointer -1: lbu a2, 0x2(a1) # read status - andi a2, a2, 0x1 # mask transmitter status - bnez a2, 1b # wait until transmitter empty - sb a0, (a1) # transmit byte - ret - - put_str: li a1, UART_BASE # set UART_BASE pointer 1: lbu a2, 0x2(a1) # read status @@ -309,25 +290,6 @@ put_str: j 1b 2: ret - -set_led_red: - li a1, LED_BASE - - # enable cr0 - li a0, CR0 - slli a0, a0, 8 - ori a0, a0, 0x80 - sh a0, (a1) - - # set pwrr - li a0, PWRR - slli a0, a0, 8 - ori a0, a0, 0xFF - sh a0, (a1) - - ret - - .section .rodata .align 4 @@ -335,15 +297,12 @@ name: .string "\n\r __ _ ____ _____\n\r / / (_)__ ___ / __/__ / ___/\n\r / /__/ / _ \\/ _ \\ _\\ \\/ _ \\/ /__ \n\r/____/_/\\___/_//_/ /___/\\___/\\___/ \n\r" copyright: - .string "\n\rStandard Semiconductor (c) 2021\n\r\n\r" - -#check_spram_str: -# .string "Checking SPRAM..." + .string "\n\rStandard Semiconductor (c) 2022\n\r\n\r" -check_flash_str: - .string "Checking FLASH..." +image_prompt: + .string "(e)xisting or (n)ew image (e/n)? " -success_str: - .string "SUCCESS\n\r" -fail_str: - .string "FAIL\n\r" \ No newline at end of file +#success_str: +# .string "SUCCESS\n\r" +#fail_str: +# .string "FAIL\n\r" diff --git a/lion-soc/demo/crt0.S b/lion-soc/demo/crt0.S new file mode 100644 index 0000000..13e3da3 --- /dev/null +++ b/lion-soc/demo/crt0.S @@ -0,0 +1,14 @@ + .section .init, "ax" + .global _start +_start: + .cfi_startproc + .cfi_undefined ra + .option push + .option norelax + la gp, __global_pointer$ + .option pop + la sp, __stack_top + add s0, sp, zero + jal zero, main + .cfi_endproc + .end diff --git a/lion-soc/demo/firmware.c b/lion-soc/demo/firmware.c new file mode 100644 index 0000000..e6d8372 --- /dev/null +++ b/lion-soc/demo/firmware.c @@ -0,0 +1,59 @@ +#include +#include +#include "firmware.h" + +void write_uart(uint8_t data) { + while (((uint8_t *) UART)[2] & 0x1); // wait until transmitter empty + *((uint8_t *) UART) = data; // transmit byte +} + +uint8_t read_uart() { + while (((uint8_t *) UART)[2] & 0x2); // wait until receiver full + return ((uint8_t *) UART)[1]; +} + +void print_char(char ch) { + write_uart((uint8_t) ch); +} + +void print_str(const char *p) { + while (*p != 0) + print_char(*p++); +} + +void print_dec(unsigned int val) { + char buffer[10]; + char *p = buffer; + while (val || p == buffer) { + *(p++) = val % 10; + val = val / 10; + } + while (p != buffer) + print_char('0' + *(--p)); +} + +void print_hex(unsigned int val, int digits) { + for (int i = (4*digits)-4; i >= 0; i -= 4) + print_char("0123456789ABCDEF"[(val >> i) % 16]); +} + +char* read_line(char* str, int n) { + uint8_t c; + int i = 0; + + if (n <= 0) + return str; + + while (i < n - 1 && ((c = read_uart()) != 0)) { // 0 ascii null + str[i++] = c; + if (c == '\n' || c == '\r') + break; + } + str[i] = '\0'; // add NUL character at end + + if (i > 0) { + return str; + } else { + return NULL; // no character at EOF + } +} diff --git a/lion-soc/demo/firmware.h b/lion-soc/demo/firmware.h new file mode 100644 index 0000000..26e2bad --- /dev/null +++ b/lion-soc/demo/firmware.h @@ -0,0 +1,20 @@ +#include + +#ifndef _FIRMWARE_H_ +#define _FIRMWARE_H_ + +#define LED ((volatile uint32_t *) 0x0) +#define UART ((volatile uint32_t *) 0x4) +#define SPI ((volatile uint32_t *) 0x8) + +void write_uart(uint8_t); +uint8_t read_uart(); + +void print_chr(char); +void print_str(const char *); +void print_dec(unsigned int); +void print_hex(unsigned int, int); + +char* read_line(char*, int); + +#endif /* _FIRMWARE_H_ */ diff --git a/lion-soc/demo/led/led.c b/lion-soc/demo/led/led.c new file mode 100644 index 0000000..ddf1d82 --- /dev/null +++ b/lion-soc/demo/led/led.c @@ -0,0 +1,55 @@ +#include +#include "../firmware.h" + +#define CR0 0x8 +#define BR 0x9 +#define ONR 0xA +#define OFR 0xB +#define BCRR 0x5 +#define BCFR 0x6 +#define PWRR 0x1 +#define PWRG 0x2 +#define PWRB 0x3 + +void write_led(uint8_t, uint8_t); +void init_led(); + +int main() { + uint8_t uart_input; + init_led(); + for(;;) { + uart_input = read_uart(); + if (uart_input == 0x72) { // 'r' + write_led(PWRR, 0xFF); + write_led(PWRG, 0x00); + write_led(PWRB, 0x00); + } else if (uart_input == 0x67) { // 'g' + write_led(PWRR, 0x00); + write_led(PWRG, 0xFF); + write_led(PWRB, 0x00); + } else if (uart_input == 0x62) { // 'b' + write_led(PWRR, 0x00); + write_led(PWRG, 0x00); + write_led(PWRB, 0xFF); + } + write_uart(uart_input); + } + return 0; +} + +void write_led(uint8_t addr, uint8_t val) { + *LED = ((uint32_t) addr << 8) | (uint32_t) val; +} + +void init_led() { + write_led(CR0, 0x80); + write_led(BR, 0x12); + write_led(ONR, 0x80); + write_led(OFR, 0x80); + write_led(BCRR, 0xCE); + write_led(BCFR, 0xCE); + // set LED off + write_led(PWRR, 0x00); + write_led(PWRG, 0x00); + write_led(PWRB, 0x00); +} diff --git a/lion-soc/demo/link.ld b/lion-soc/demo/link.ld new file mode 100644 index 0000000..51f1981 --- /dev/null +++ b/lion-soc/demo/link.ld @@ -0,0 +1,249 @@ +/* Script for -z combreloc: combine and sort reloc sections */ +/* Copyright (C) 2014-2018 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", + "elf32-littleriscv") +OUTPUT_ARCH(riscv) +MEMORY +{ + RAM (rwx) : ORIGIN = 0x20000, LENGTH = 0x20000 +} +ENTRY(_start) +SEARCH_DIR("/opt/riscv/riscv32-unknown-elf/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400)); . = SEGMENT_START("text-segment", 0x400) + SIZEOF_HEADERS; + PROVIDE(__stack_top = ORIGIN(RAM) + LENGTH(RAM)); + + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) + *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) + *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) + *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .rela.plt : + { + *(.rela.plt) + } + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : + { + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table + .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges + .exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + . = DATA_SEGMENT_RELRO_END (0, .); + .data : + { + __DATA_BEGIN__ = .; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + __SDATA_BEGIN__ = .; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + . = ALIGN(32 / 8); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(32 / 8); + __BSS_END__ = .; + __global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800, + MAX(__DATA_BEGIN__ + 0x800, __BSS_END__ - 0x800)); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} + diff --git a/lion-soc/lion-soc.cabal b/lion-soc/lion-soc.cabal index bfcd387..476868b 100644 --- a/lion-soc/lion-soc.cabal +++ b/lion-soc/lion-soc.cabal @@ -74,3 +74,13 @@ executable com default-extensions: LambdaCase default-language: Haskell2010 + +executable boot + main-is: Boot.hs + build-depends: async >= 2.2 && < 2.3, + base, + bytestring, + serialport >= 0.5 && < 0.6, + stm + hs-source-dirs: app + default-language: Haskell2010 diff --git a/lion-soc/src/Uart.hs b/lion-soc/src/Uart.hs index a45ac45..7103e44 100644 --- a/lion-soc/src/Uart.hs +++ b/lion-soc/src/Uart.hs @@ -12,7 +12,7 @@ import Clash.Prelude import qualified Bus as B import Control.Lens hiding (Index, Empty) import Control.Monad.RWS -import Data.Maybe ( isJust, fromMaybe ) +import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid.Generic -- | uart register @@ -20,7 +20,7 @@ import Data.Monoid.Generic -- resvd : status : recv : send -- -- bits 31 - 24: reserved --- bits 23 - 16: status byte, bit 17 - 0 = receiver empty, 1 = receiver full +-- bits 23 - 16: status byte, bit 17 - 0 = receiver full, 1 = receiver empty -- bit 16 - 0 = transmitter empty (idle), 1 = transmitter full (busy) -- status byte is read only -- bits 15 - 8 : receiver buffer -- read only -- reading this byte will reset the receiver @@ -107,7 +107,7 @@ uartM = do _ -> return () -- read status - rxS <- uses rxRecv $ boolToBV . isJust + rxS <- uses rxRecv $ boolToBV . isNothing let txS = boolToBV $ isJust bufferM status = (rxS `shiftL` 1) .|. txS scribe toCore $ First $ Just $ status `shiftL` 16