diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d9469a..1adb0f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,7 @@ if(PROJECT_IS_TOP_LEVEL) # Bitstream generation - set(CLOCK_FREQUENCY 60) + set(CLOCK_FREQUENCY 68) add_ecp5_pll_generation( INPUT_FREQ 24 OUTPUT_FREQ ${CLOCK_FREQUENCY} diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index ddd8ca7..88846dd 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -56,8 +56,7 @@ add_custom_command( add_custom_target(helloworld DEPENDS helloworld.dump helloworld.load) - -add_executable(bootloader.elf ${CMAKE_CURRENT_LIST_DIR}/bootloader/bootloader.S ${CMAKE_CURRENT_LIST_DIR}/bootloader/uart.S ${CMAKE_CURRENT_LIST_DIR}/bootloader/spi.S) +add_executable(bootloader.elf ${CMAKE_CURRENT_LIST_DIR}/bootloader/bootloader.S ${CMAKE_CURRENT_LIST_DIR}/bootloader/uart.S ${CMAKE_CURRENT_LIST_DIR}/bootloader/spi.S ${CMAKE_CURRENT_LIST_DIR}/bootloader/flash.S ${CMAKE_CURRENT_LIST_DIR}/bootloader/elf.S) set_target_properties(bootloader.elf PROPERTIES COMPILE_FLAGS "${CC_OPTS} -march=rv32g -mabi=ilp32" LINK_FLAGS "${CC_OPTS} -march=rv32g -mabi=ilp32 -T${CMAKE_CURRENT_LIST_DIR}/link.ld") diff --git a/firmware/bootloader/bootloader.S b/firmware/bootloader/bootloader.S index 3a683b1..d6dd042 100644 --- a/firmware/bootloader/bootloader.S +++ b/firmware/bootloader/bootloader.S @@ -19,61 +19,62 @@ # You should have received a copy of the GNU General Public License # along with ECAP5-DSOC. If not, see . +.equ FLASH_MFID, 0xEF +.equ FLASH_DEVTYPE, 0x40 +.equ FLASH_SIZE, 0x18 + +.equ EXE_ADDR, 0x300000 + .section .text .global _start _start: + la sp, __BOOT_STACK_TOP__ + call configure_uart call configure_spi - call enable_cs - - la a0, boot_str - call send_string - la a0, readingflash_str - call send_string + # Checking the flash ID + li a0, FLASH_MFID + li a1, FLASH_DEVTYPE + li a2, FLASH_SIZE + call check_flash_id + bne a0, x0, _booterr - li a0, 0x9F - call send_spi + # Checking ELF + li a0, EXE_ADDR + call check_elf_type + bne a0, x0, _booterr - li a0, 0x00 - call send_spi - mv s0, a0 + # Loading ELF in RAM + li a0, EXE_ADDR + call load_elf_in_ram + bne a0, x0, _booterr - li a0, 0x00 - call send_spi - mv s1, a0 + # Get the ELF entry point + la a0, EXE_ADDR + call get_elf_entry - li a0, 0x00 - call send_spi - mv s2, a0 + # Move the entry to ra + mv ra, a0 - la a0, mfid_str - call send_string - - mv a0, s0 - call send_hex + # Set the stack pointer + la sp, __RAM_END__ - la a0, type_str - call send_string + # Clear registers (except x0, ra and sp) + .irp r, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + mv x\r, x0 + .endr - mv a0, s1 - call send_hex + # Jump to ra + ret - la a0, size_str +_booterr: + la a0, bootfail_str call send_string - - mv a0, s2 - call send_hex - - call disable_cs - -_loop: +_loop: j _loop .align 4 -boot_str: .asciz "\n\nBooting..." -readingflash_str: .asciz "\nReading flash..." -mfid_str: .asciz "\nManufacturer ID : " -type_str: .asciz "\nType : " -size_str: .asciz "\nSize : " +bootfail_str: .asciz "\n[BOOT] FAIL\n" +pon_str: .asciz "\n[BOOT] FAIL\n" diff --git a/firmware/bootloader/elf.S b/firmware/bootloader/elf.S new file mode 100644 index 0000000..97992e7 --- /dev/null +++ b/firmware/bootloader/elf.S @@ -0,0 +1,286 @@ +# __ _ +# ________/ / ___ _(_)__ ___ +# / __/ __/ _ \/ _ `/ / _ \/ -_) +# \__/\__/_//_/\_,_/_/_//_/\__/ +# +# Copyright (C) Clément Chaine +# This file is part of ECAP5-DSOC +# +# ECAP5-DSOC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ECAP5-DSOC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ECAP5-DSOC. If not, see . + +.equ ELF_CLASS_OFFSET, 0x4 +.equ ELF_MACHINE_OFFSET, 0x12 +.equ ELF_PHOFF_OFFSET, 0x1C +.equ ELF_PHNUM_OFFSET, 0x2C +.equ ELF_ENTRY_OFFSET, 0x18 + +.equ ELF_SEGMENT_HEADER_SIZE, 32 +.equ ELF_SEGMENT_TYPE_PTLOAD, 1 + +.equ ELF_CLASS_ELF32, 1 +.equ ELF_MACHINE_RISCV, 0xF3 + +# +# Checks the elf type +# a0 : address in flash +# +.global check_elf_type +check_elf_type: + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + # local 4 byte buffer at offset 4 + + # s0 = address of the elf in flash + mv s0, a0 + + # Read the elf magic (4 bytes) + mv a0, s0 # src + addi a1, sp, 4 # dst + li a2, 3 # size + call copy_flash_to_ram + + # Compare the elf magic + la t0, elf_magic + lw t1, 0(t0) + lw t2, 4(sp) + # Clear the 4th byte + li t3, 0xFFFFFF + and t1, t1, t3 + and t2, t2, t3 + bne t1, t2, _bad_magic + + # Read the elf class + addi a0, s0, ELF_CLASS_OFFSET + addi a1, sp, 4 + li a2, 1 + call copy_flash_to_ram + + # Compare the elf class + li t0, ELF_CLASS_ELF32 + mv t1, x0 + lbu t1, 4(sp) + bne t0, t1, _bad_class + + # Read the elf machine + addi a0, s0, ELF_MACHINE_OFFSET + addi a1, sp, 4 + li a2, 1 + call copy_flash_to_ram + + # Compare the elf class + li t0, ELF_MACHINE_RISCV + mv t1, x0 + lbu t1, 4(sp) + bne t0, t1, _bad_machine + + # Set the return value + mv a0, x0 + + j _check_elf_ret + +_bad_magic: + la a0, bad_magic_str + call send_string + + j _check_elf_ret_bad + +_bad_class: + la a0, bad_class_str + call send_string + + j _check_elf_ret_bad + +_bad_machine: + la a0, bad_machine_str + call send_string + +_check_elf_ret_bad: + # Set the return value + li a0, 1 +_check_elf_ret: + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + + ret + +# +# Copies the ELF in RAM +# a0 : address in flash +# +.global load_elf_in_ram +load_elf_in_ram: + addi sp, sp, -64 + sw ra, 60(sp) + sw s0, 56(sp) + sw s1, 52(sp) + sw s2, 48(sp) + # 32 byte array at offset 0(sp) to store a segment header + + # s0 = address of the elf in flash + mv s0, a0 + # s1 = number of segments + mv s1, x0 + + # Read the number of entries in the table + addi a0, s0, ELF_PHNUM_OFFSET + mv a1, sp + li a2, 2 + call copy_flash_to_ram + # Store the number of entries + mv s1, x0 + lhu s1, 0(sp) + + # Read the program header table offset + addi a0, s0, ELF_PHOFF_OFFSET + mv a1, sp + li a2, 4 + call copy_flash_to_ram + # Store the address of the program header table + lw t0, 0(sp) + add s2, s0, t0 + + # s2 = address of the program header table + +_read_segment: + # Read the segment header + mv a0, s2 + mv a1, sp + li a2, ELF_SEGMENT_HEADER_SIZE + call copy_flash_to_ram + + # Skip the copy if the segment is not PT_LOAD + li t0, ELF_SEGMENT_TYPE_PTLOAD + lw t1, 0(sp) # p_type + bne t0, t1, _next_segment + + # Skip if the destination address is not in RAM + lw t0, 0x8(sp) # p_vaddr + lw t1, 0x14(sp) # p_memsz + add t1, t0, t1 # p_vaddr + p_memsz + la t2, __RAM_START__ + la t3, __RAM_END__ + blt t0, t2, _segment_not_in_ram + bgt t1, t3, _segment_not_in_ram + + # Error if trying to overwrite bootloader + la t2, __BOOT_START__ + bge t0, t2, _bootloader_overwrite + bgt t1, t2, _bootloader_overwrite + + # Skip the copy if the segment file size is zero + lw t0, 0x10(sp) + beq t0, x0, _skip_copy + + # Copy the segment data in RAM + lw a0, 0x4(sp) # p_offset + add a0, a0, s0 # base + p_offset + lw a1, 0x8(sp) # p_vaddr + lw a2, 0x10(sp) # p_filesz + call copy_flash_to_ram + +_skip_copy: + # Check if addresses need to be cleared + lw t0, 0x14(sp) # p_memsz + lw t1, 0x10(sp) # p_filesz + beq t0, t1, _next_segment + + # Prepare the clear + sub t0, t0, t1 # p_memsz - p_filesz + lw t2, 0x8(sp) # p_vaddr + add t1, t2, t1 # p_vaddr + p_filesz + +_clear_bss: + # Write zeros (1 instead of 4 as sizes may vary) + sb x0, 0(t1) + + # Move to next address to clear + addi t1, t1, 1 + # Decrement the number of addresses to clear + addi t0, t0, -1 + + # Check if last address to clear + bne t0, x0, _clear_bss + + j _next_segment + +_segment_not_in_ram: + la a0, segnotinram_str + call send_string + + mv a0, s1 + call send_hex + +_next_segment: + # Move to the next segment + addi s2, s2, ELF_SEGMENT_HEADER_SIZE + # Decrement the number of segments left + addi s1, s1, -1 + + # Check if last segment + bne s1, x0, _read_segment + + # Load the return value + mv a0, x0 + + j _load_elf_in_ram_ret + +_bootloader_overwrite: + la a0, bootoverwrite_str + call send_string + + # Load the return value + li a0, 1 + +_load_elf_in_ram_ret: + lw ra, 60(sp) + lw s0, 56(sp) + lw s1, 52(sp) + lw s2, 48(sp) + addi sp, sp, 64 + + ret + +# +# Returns the elf entry point +# a0 : address in flash +# +.global get_elf_entry +get_elf_entry: + addi sp, sp, -16 + sw ra, 12(sp) + # 1 byte used as local table at offset 0 + + # Get the entry field + addi a0, a0, ELF_ENTRY_OFFSET # base + e_entry + mv a1, sp + li a2, 4 + call copy_flash_to_ram + + # Set the return value + lw a0, 0(sp) + + lw ra, 12(sp) + addi sp, sp, 16 + ret + +.align 4 + +elf_magic: .byte 0x7F, 'E', 'L', 'F' +bad_magic_str: .asciz "\n[BOOT] BAD MAGIC " +bad_class_str: .asciz "\n[BOOT] BAD CLASS " +bad_machine_str: .asciz "\n[BOOT] BAD MACHINE " +segnotinram_str: .asciz "\n[BOOT] WARN SEGMENT NOT IN RAM " +bootoverwrite_str: .asciz "\n[BOOT] ERROR BOOT OVERWRITE " diff --git a/firmware/bootloader/flash.S b/firmware/bootloader/flash.S new file mode 100644 index 0000000..965d99f --- /dev/null +++ b/firmware/bootloader/flash.S @@ -0,0 +1,191 @@ +# __ _ +# ________/ / ___ _(_)__ ___ +# / __/ __/ _ \/ _ `/ / _ \/ -_) +# \__/\__/_//_/\_,_/_/_//_/\__/ +# +# Copyright (C) Clément Chaine +# This file is part of ECAP5-DSOC +# +# ECAP5-DSOC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ECAP5-DSOC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ECAP5-DSOC. If not, see . + +.section .text + +# +# Checks the flash JEDEC identification +# a0 : manufacturer id +# a1 : device type +# a2 : device size +# +.global check_flash_id +check_flash_id: + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + sw s1, 20(sp) + sw s2, 16(sp) + sw s3, 12(sp) + sw s4, 8(sp) + sw s5, 4(sp) + + # s0 = expected manufacturer id + mv s0, a0 + # s1 = expected device id + mv s1, a1 + # s2 = expected device size + mv s2, a2 + + call enable_cs + + # send the read Jedec ID command + li a0, 0x9F + call send_spi + # read the manufacturer id + li a0, 0x00 + call send_spi + mv s3, a0 + # read the device type + li a0, 0x00 + call send_spi + mv s4, a0 + # read the device size + li a0, 0x00 + call send_spi + mv s5, a0 + + call disable_cs + + # If the manufacturer id is wrong + bne s0, s3, _display_bad + # If the device type is wrong + bne s1, s4, _display_bad + # If the device size is wrong + bne s2, s5, _display_bad + + # set the return value = 0 + mv a0, x0 + + j _check_flash_id_ret + +_display_bad: + la a0, badflash_str + call send_string + + # display the manufacturer id + la a0, mfid_str + call send_string + + mv a0, s3 + call send_hex + + # display the device type + la a0, type_str + call send_string + + mv a0, s4 + call send_hex + + # display the device size + la a0, size_str + call send_string + + mv a0, s5 + call send_hex + + # set the return value = 1 + li a0, 1 + +_check_flash_id_ret: + lw ra, 28(sp) + lw s0, 24(sp) + lw s1, 20(sp) + lw s2, 16(sp) + lw s3, 12(sp) + lw s4, 8(sp) + lw s5, 4(sp) + addi sp, sp, 32 + + ret + +# +# Copies data from flash to ram +# a0 : src address in flash +# a1 : dst address in ram +# a2 : size in bytes +# +.global copy_flash_to_ram +copy_flash_to_ram: + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + sw s1, 4(sp) + sw s2, 0(sp) + + # s0 = src + mv s0, a0 + # s1 = dst + mv s1, a1 + # s2 = size + mv s2, a2 + + call enable_cs + + # send fast read command + li a0, 0xB + call send_spi + # send fast read address A23-A16 + srli a0, s0, 16 + andi a0, a0, 0xFF + call send_spi + # send fast read address A15-A8 + srli a0, s0, 8 + andi a0, a0, 0xFF + call send_spi + # send fast read address A7-A0 + andi a0, s0, 0xFF + call send_spi + # send dummy byte + li a0, 0x00 + call send_spi +_copy: + # Load a value from flash + li a0, 0x00 + call send_spi + # a0 contains the read value + sb a0, 0(s1) + + # Increment the dst + addi s1, s1, 1 + # Decrement the size + addi s2, s2, -1 + + # Check if done + bne s2, x0, _copy + +_copy_flash_to_ram_ret: + call disable_cs + + lw ra, 12(sp) + lw s0, 8(sp) + lw s1, 4(sp) + lw s2, 0(sp) + addi sp, sp, 16 + + ret + +.align 4 + +badflash_str: .asciz "\n[BOOT] BAD FLASH" +mfid_str: .asciz "\n Manufacturer ID : " +type_str: .asciz "\n Type : " +size_str: .asciz "\n Size : " diff --git a/firmware/bootloader/spi.S b/firmware/bootloader/spi.S index d07ec86..752bd35 100644 --- a/firmware/bootloader/spi.S +++ b/firmware/bootloader/spi.S @@ -110,8 +110,8 @@ _check_send_done: beq t1, x0, _check_send_done _read_response: - lb a0, SPI_RXDR(t4) - andi a0, a0, 0xFF + mv a0, x0 + lbu a0, SPI_RXDR(t4) ret diff --git a/firmware/bootloader/uart.S b/firmware/bootloader/uart.S index 275b234..40ad04b 100644 --- a/firmware/bootloader/uart.S +++ b/firmware/bootloader/uart.S @@ -30,7 +30,7 @@ .equ UART_DS_MASK, (1 << 3) .equ UART_TXE_MASK, (1 << 1) -.equ UART_ACC_INCR, 10 +.equ UART_ACC_INCR, 111 .equ UART_DS, 1 .section .text @@ -74,7 +74,7 @@ _check_send_ready_string: _send_char: # Load the char to send - lb t0, 0(t5) + lbu t0, 0(t5) # If the string is fully sent return beq t0, x0, _return_send_string diff --git a/firmware/link.ld b/firmware/link.ld index 739b83d..ffe297c 100644 --- a/firmware/link.ld +++ b/firmware/link.ld @@ -1,8 +1,43 @@ +/* __ _ + * ________/ / ___ _(_)__ ___ + * / __/ __/ _ \/ _ `/ / _ \/ -_) + * \__/\__/_//_/\_,_/_/_//_/\__/ + * + * Copyright (C) Clément Chaine + * This file is part of ECAP5-DSOC + * + * ECAP5-DSOC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ECAP5-DSOC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ECAP5-DSOC. If not, see . + */ + OUTPUT_ARCH( "riscv" ) ENTRY(_start) - +MEMORY +{ + RAM (rwx) : ORIGIN = 0x0, LENGTH = 0x68000 + BOOT (rwx) : ORIGIN = 0x68000-4K, LENGTH = 4K +} SECTIONS { - .text : { *(.text) } + .text : { + *(.text) + } > BOOT } +__BOOT_STACK_TOP__ = ORIGIN(BOOT) + LENGTH(BOOT); + +__RAM_START__ = ORIGIN(RAM); +__RAM_END__ = ORIGIN(RAM) + LENGTH(RAM); + +__BOOT_START__ = ORIGIN(BOOT); +__BOOT_END__ = ORIGIN(BOOT) + LENGTH(BOOT); diff --git a/src/ecap5_dsoc.sv b/src/ecap5_dsoc.sv index b372966..0315d5c 100644 --- a/src/ecap5_dsoc.sv +++ b/src/ecap5_dsoc.sv @@ -134,7 +134,8 @@ generated_pll pll_inst ( ); ecap5_dproc #( - .BOOT_ADDRESS (32'h0) + // last 4K region for boot + .BOOT_ADDRESS (32'h67000) ) core_inst ( .clk_i (clk), .rst_i (rst), diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4f13b16..99decf6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,56 +19,6 @@ # You should have received a copy of the GNU General Public License # along with ECAP5-DSOC. If not, see . -# Bench parent directory -set(BENCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/benches/) -# C++ bench include directory -set(TEST_INCLUDE_DIRS ${ecap5_ecap5_dtestlib_SOURCE_DIR}/include) -# Bench CSV output directory -set(TESTDATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/) +add_subdirectory(benches) +add_subdirectory(programs) -# Lint -add_lint_target( - TARGET lint - LIB ecap5_dsoc -) - -# Generate a mocked pll -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generated_pll.v - "module generated_pll(\n" - " input clkin,\n" - " output clkout0,\n" - " output locked\n" - ");\n" - "logic locked_q = 1'b0;\n" - "logic locked_qq;\n" - "always_ff @(posedge clkin) begin\n" - " locked_q <= 1;\n" - " locked_qq <= locked_q;\n" - "end\n" - "assign clkout0 = clkin;\n" - "assign locked = locked_qq;\n" - "endmodule\n" -) -# Create a test interface library -add_library(ecap5_dsoc_tests INTERFACE) -target_sources(ecap5_dsoc_tests INTERFACE - ${CMAKE_CURRENT_BINARY_DIR}/generated_pll.v -) -target_link_libraries(ecap5_dsoc_tests INTERFACE - ecap5_dsoc -) - -# Add testbenches -add_testbench( - MODULE ecap5_dsoc - LIBS ecap5_dsoc_tests - BENCH_DIR ${BENCH_DIR} - TESTDATA_DIR ${TESTDATA_DIR} - TEST_INCLUDE_DIRS ${TEST_INCLUDE_DIRS} - DEPENDS bootloader - DEFINES BOOTLOADER_PATH=\\\"${CMAKE_BINARY_DIR}/firmware/bootloader.load\\\" -) - -# Main targets -add_custom_target(build DEPENDS ${TEST_BINARIES}) -add_custom_target(tests DEPENDS ${TEST_TARGETS}) diff --git a/tests/benches/CMakeLists.txt b/tests/benches/CMakeLists.txt new file mode 100644 index 0000000..1273f19 --- /dev/null +++ b/tests/benches/CMakeLists.txt @@ -0,0 +1,74 @@ +# __ _ +# ________/ / ___ _(_)__ ___ +# / __/ __/ _ \/ _ `/ / _ \/ -_) +# \__/\__/_//_/\_,_/_/_//_/\__/ +# +# Copyright (C) Clément Chaine +# This file is part of ECAP5-DSOC +# +# ECAP5-DSOC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ECAP5-DSOC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ECAP5-DSOC. If not, see . + +# Bench parent directory +set(BENCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/) +# C++ bench include directory +set(TEST_INCLUDE_DIRS ${ecap5_ecap5_dtestlib_SOURCE_DIR}/include) +# Bench CSV output directory +set(TESTDATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/../) + +# Lint +add_lint_target( + TARGET lint + LIB ecap5_dsoc +) + +# Generate a mocked pll +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generated_pll.v + "module generated_pll(\n" + " input clkin,\n" + " output clkout0,\n" + " output locked\n" + ");\n" + "logic locked_q = 1'b0;\n" + "logic locked_qq;\n" + "always_ff @(posedge clkin) begin\n" + " locked_q <= 1;\n" + " locked_qq <= locked_q;\n" + "end\n" + "assign clkout0 = clkin;\n" + "assign locked = locked_qq;\n" + "endmodule\n" +) +# Create a test interface library +add_library(ecap5_dsoc_tests INTERFACE) +target_sources(ecap5_dsoc_tests INTERFACE + ${CMAKE_CURRENT_BINARY_DIR}/generated_pll.v +) +target_link_libraries(ecap5_dsoc_tests INTERFACE + ecap5_dsoc +) + +# Add testbenches +add_testbench( + MODULE ecap5_dsoc + LIBS ecap5_dsoc_tests + BENCH_DIR ${BENCH_DIR} + TESTDATA_DIR ${TESTDATA_DIR} + TEST_INCLUDE_DIRS ${TEST_INCLUDE_DIRS} + DEPENDS bootloader + DEFINES BOOTLOADER_PATH=\\\"${CMAKE_BINARY_DIR}/firmware/bootloader.load\\\" +) + +# Main targets +add_custom_target(build DEPENDS ${TEST_BINARIES}) +add_custom_target(tests DEPENDS ${TEST_TARGETS}) diff --git a/tests/benches/ecap5_dsoc/tb_ecap5_dsoc.cpp b/tests/benches/ecap5_dsoc/tb_ecap5_dsoc.cpp index e11325b..26cd0d2 100644 --- a/tests/benches/ecap5_dsoc/tb_ecap5_dsoc.cpp +++ b/tests/benches/ecap5_dsoc/tb_ecap5_dsoc.cpp @@ -69,8 +69,8 @@ int main(int argc, char ** argv, char ** env) { /************************************************************/ tb->reset(); - // 10ms - tb->n_tick(600000); + // 1ms + tb->n_tick(60000); /************************************************************/ diff --git a/tests/programs/CMakeLists.txt b/tests/programs/CMakeLists.txt new file mode 100644 index 0000000..5277c92 --- /dev/null +++ b/tests/programs/CMakeLists.txt @@ -0,0 +1,43 @@ +# __ _ +# ________/ / ___ _(_)__ ___ +# / __/ __/ _ \/ _ `/ / _ \/ -_) +# \__/\__/_//_/\_,_/_/_//_/\__/ +# +# Copyright (C) Clément Chaine +# This file is part of ECAP5-DSOC +# +# ECAP5-DSOC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ECAP5-DSOC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ECAP5-DSOC. If not, see . + +set(CROSS_COMPILE riscv64-unknown-elf-) +set(CMAKE_ASM_COMPILER ${CROSS_COMPILE}gcc) +set(CMAKE_AR ${CROSS_COMPILE}ar) +set(CMAKE_ASM_COMPILER ${CROSS_COMPILE}gcc) +set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc) +set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++) + +enable_language(ASM) + +set(CC_OPTS "-static \ + -mcmodel=medany \ + -fvisibility=hidden \ + -nostdlib \ + -nostartfiles") + +set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/main.c) +set(LD_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/link.ld) + +add_executable(main.elf ${SOURCES}) +set_target_properties(main.elf + PROPERTIES COMPILE_FLAGS "${CC_OPTS} -march=rv32g -mabi=ilp32" + LINK_FLAGS "${CC_OPTS} -march=rv32g -mabi=ilp32 -T${LD_SCRIPT}") diff --git a/tests/programs/README.md b/tests/programs/README.md new file mode 100644 index 0000000..783c9d3 --- /dev/null +++ b/tests/programs/README.md @@ -0,0 +1,5 @@ +# How to load in flash + +```bash +ecpprog -o 3M main.elf +``` diff --git a/tests/programs/link.ld b/tests/programs/link.ld new file mode 100644 index 0000000..e5ce43a --- /dev/null +++ b/tests/programs/link.ld @@ -0,0 +1,53 @@ +/* __ _ + * ________/ / ___ _(_)__ ___ + * / __/ __/ _ \/ _ `/ / _ \/ -_) + * \__/\__/_//_/\_,_/_/_//_/\__/ + * + * Copyright (C) Clément Chaine + * This file is part of ECAP5-DSOC + * + * ECAP5-DSOC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ECAP5-DSOC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ECAP5-DSOC. If not, see . + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(main) +MEMORY +{ + RAM (rwx) : ORIGIN = 0x0, LENGTH = 0x1A000 + /* the flash is not memory mapped, but giving it an address + * will help determine what needs to be loaded in ram */ + FLASH (r) : ORIGIN = 0x10000000, LENGTH = 0xD00000 +} +SECTIONS +{ + .text : { + *(.text) + } > RAM AT > FLASH + + .rodata : { + *(.rodata) + } > RAM AT > FLASH + + .data : { + *(.data) + *(.sdata) + } > RAM AT > FLASH + + .bss : { + *(.bss) + *(.sbss) + } > RAM AT > FLASH + + _stack_top = ORIGIN(RAM) + LENGTH(RAM); +} diff --git a/tests/programs/main.c b/tests/programs/main.c new file mode 100644 index 0000000..a8a64a9 --- /dev/null +++ b/tests/programs/main.c @@ -0,0 +1,54 @@ +/* __ _ + * ________/ / ___ _(_)__ ___ + * / __/ __/ _ \/ _ `/ / _ \/ -_) + * \__/\__/_//_/\_,_/_/_//_/\__/ + * + * Copyright (C) Clément Chaine + * This file is part of ECAP5-DSOC + * + * ECAP5-DSOC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ECAP5-DSOC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ECAP5-DSOC. If not, see . + */ + +#include +#include + +#define UART_BASE 0x40000000 +#define UART_SR_TXE_MASK (1 << 1) + +typedef struct { + volatile uint32_t sr; + volatile uint32_t cr; + volatile uint32_t rxdr; + volatile uint32_t txdr; +} uart_regs_t; + +void send_string(char * str) { + uart_regs_t * uart = (uart_regs_t *)UART_BASE; + + char * c = str; + while(*c != '\0') { + // Wait for the uart to be ready + while(!(uart->sr & UART_SR_TXE_MASK)) {} + + uart->txdr = *c; + + c += 1; + } +} + +void main(void) { + send_string("Hello, World!\n"); + + while(1); +}