ar100: Support for ar100 (#6054)

Add files to support AR100

Signed-off-by: Elias Bakken <elias@iagent.no>
This commit is contained in:
Elias Bakken 2023-02-21 02:15:01 +01:00 committed by GitHub
parent d7bd7f1f4b
commit b7978d37b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 3715 additions and 0 deletions

View File

@ -354,6 +354,27 @@ micro-controller.
| 1 stepper (200Mhz) | 39 | | 1 stepper (200Mhz) | 39 |
| 3 stepper (200Mhz) | 181 | | 3 stepper (200Mhz) | 181 |
### AR100 step rate benchmark ###
The following configuration sequence is used on AR100 CPU (Allwinner A64):
```
allocate_oids count=3
config_stepper oid=0 step_pin=PL10 dir_pin=PE14 invert_step=-1 step_pulse_ticks=0
config_stepper oid=1 step_pin=PL11 dir_pin=PE15 invert_step=-1 step_pulse_ticks=0
config_stepper oid=2 step_pin=PL12 dir_pin=PE16 invert_step=-1 step_pulse_ticks=0
finalize_config crc=0
```
The test was last run on commit `08d037c6` with gcc version
`or1k-linux-musl-gcc (GCC) 9.2.0` on an Allwinner A64-H
micro-controller.
| AR100 R_PIO | ticks |
| -------------------- | ----- |
| 1 stepper | 85 |
| 3 stepper | 359 |
### RP2040 step rate benchmark ### RP2040 step rate benchmark
The following configuration sequence is used on the RP2040: The following configuration sequence is used on the RP2040:
@ -425,6 +446,7 @@ hub.
| atmega2560 (serial) | 23K | b161a69e | avr-gcc (GCC) 4.8.1 | | atmega2560 (serial) | 23K | b161a69e | avr-gcc (GCC) 4.8.1 |
| sam3x8e (serial) | 23K | b161a69e | arm-none-eabi-gcc (Fedora 7.1.0-5.fc27) 7.1.0 | | sam3x8e (serial) | 23K | b161a69e | arm-none-eabi-gcc (Fedora 7.1.0-5.fc27) 7.1.0 |
| at90usb1286 (USB) | 75K | 01d2183f | avr-gcc (GCC) 5.4.0 | | at90usb1286 (USB) | 75K | 01d2183f | avr-gcc (GCC) 5.4.0 |
| ar100 (serial) | 138K | 08d037c6 | or1k-linux-musl-gcc 9.3.0 |
| samd21 (USB) | 223K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 | | samd21 (USB) | 223K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| pru (shared memory) | 260K | c5968a08 | pru-gcc (GCC) 8.0.0 20170530 (experimental) | | pru (shared memory) | 260K | c5968a08 | pru-gcc (GCC) 8.0.0 20170530 (experimental) |
| stm32f103 (USB) | 355K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 | | stm32f103 (USB) | 355K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |

View File

@ -185,6 +185,7 @@ represent total number of steps per second on the micro-controller.
| RP2040 | 2400K | 1636K | | RP2040 | 2400K | 1636K |
| SAM4E8E | 2500K | 1674K | | SAM4E8E | 2500K | 1674K |
| SAMD51 | 3077K | 1885K | | SAMD51 | 3077K | 1885K |
| AR100 | 3529K | 2507K |
| STM32F407 | 3652K | 2459K | | STM32F407 | 3652K | 2459K |
| STM32F446 | 3913K | 2634K | | STM32F446 | 3913K | 2634K |
| STM32H743 | 9091K | 6061K | | STM32H743 | 9091K | 6061K |

View File

@ -125,6 +125,10 @@ from the repo's hc-sr04-range-sensor directory. It has been modified
so that the IEP definitions compile correctly. See pru_rpmsg.patch for so that the IEP definitions compile correctly. See pru_rpmsg.patch for
the modifications. the modifications.
The ar100 directory contains code from:
https://github.com/crust-firmware/crust
revision 966124af914ce611aadd06fbbcbc4c36c4a0b240
The fast-hash directory contains code from: The fast-hash directory contains code from:
https://github.com/ztanml/fast-hash https://github.com/ztanml/fast-hash
revision ae3bb53c199fe75619e940b5b6a3584ede99c5fc revision ae3bb53c199fe75619e940b5b6a3584ede99c5fc

2667
lib/ar100/asm/spr.h Normal file

File diff suppressed because it is too large Load Diff

38
lib/ar100/macros.S Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright © 2013-2017, ARM Limited and Contributors. All rights reserved.
* Copyright © 2017-2020 The Crust Firmware Authors.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef MACROS_S
#define MACROS_S
/* This macro marks a global data declaration. */
.macro data name
.section .data.\name, "aw", @progbits
.global \name
.type \name, %object
.align 4
\name:
.endm
/* This macro marks the beginning of a function. */
.macro func name
.section .text.\name, "ax", @progbits
.global \name
.type \name, %function
.func \name
.cfi_sections .debug_frame
.cfi_startproc
.align 4
\name:
.endm
/* This macro marks the end of a function. */
.macro endfunc name
.cfi_endproc
.endfunc
.size \name, . - \name
.endm
#endif /* MACROS_S */

117
lib/ar100/runtime.S Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright © 2017-2020 The Crust Firmware Authors.
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only
*/
#include <macros.S>
func __divsi3
l.sflts r3, r0
l.sw -8(r1), r18
l.bnf 1f
l.ori r18, r0, 0
l.sub r3, r0, r3 # Negate x if it is negative
l.addi r18, r18, 1 # Increment the flag if x is negative
1: l.sflts r4, r0
l.bnf 2f
l.sw -4(r1), r9
l.sub r4, r0, r4 # Negate y if it is negative
l.addi r18, r18, -1 # Decrement the flag if y is negative
2: l.jal __udivsi3
l.addi r1, r1, -8
l.sfne r18, r0
l.bnf 3f
l.addi r1, r1, 8
l.sub r11, r0, r11 # Negate q if the flag is nonzero
3: l.lwz r9, -4(r1)
l.jr r9
l.lwz r18, -8(r1)
endfunc __divsi3
/*
* Of the three ORBIS32 32-bit multiplication instructions (l.mul, l.muli, and
* l.mulu), only l.mul works. By passing "-msoft-mul" to the compiler, and
* delegating to this function, we can force all multiplication to use l.mul.
*/
func __mulsi3
l.jr r9
l.mul r11, r3, r4
endfunc __mulsi3
/*
* Derived from the "best method for counting bits in a 32-bit integer" at
* https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel.
*
* Signed multiplication is used because l.mulu is broken in hardware. This is
* safe because the previous bit masking ensures neither operand is negative.
*/
func __popcountsi2
l.movhi r5, 0x5555 # Statement 1:
l.ori r5, r5, 0x5555 # r5 = 0x55555555
l.srli r4, r3, 1 # r4 = v >> 1
l.and r4, r4, r5 # r4 = (v >> 1) & 0x55555555
l.sub r3, r3, r4 # v = v - ((v >> 1) & 0x55555555)
l.movhi r5, 0x3333 # Statement 2:
l.ori r5, r5, 0x3333 # r5 = 0x33333333
l.srli r4, r3, 2 # r4 = v >> 2
l.and r4, r4, r5 # r4 = (v >> 2) & 0x33333333
l.and r3, r3, r5 # v = v & 0x33333333
l.add r3, r3, r4 # v += ((v >> 2) & 0x33333333)
l.movhi r5, 0x0f0f # Statement 3:
l.ori r5, r5, 0x0f0f # r5 = 0x0f0f0f0f
l.srli r4, r3, 4 # r4 = v >> 4
l.add r4, r3, r4 # r4 = v + (v >> 4)
l.and r4, r4, r5 # r4 = v + (v >> 4) & 0x0f0f0f0f
l.movhi r5, 0x0101
l.ori r5, r5, 0x0101 # r5 = 0x01010101
l.mul r11, r4, r5 # c = r4 * 0x01010101
l.jr r9
l.srli r11, r11, 24 # return c >> 24
endfunc __popcountsi2
/*
* Optimized implementation of the "shift divisor method" algorithm from
* T. Rodeheffer. Software Integer Division. Microsoft Research, 2008.
*
* In addition to returning the quotient in r11, this function also returns
* the remainder in r12. __umodsi3 simply copies the remainder into r11.
*/
func __udivsi3 # u32 __udivsi3(u32 x, u32 y) {
l.sfeqi r4, 1 # if (y == 1)
l.bf 5f # goto identity;
l.ori r12, r3, 0 # u32 r = x;
l.ori r5, r4, 0 # u32 y0 = y;
l.addi r11, r0, 0 # u32 q = 0;
l.sfltu r3, r4 # if (x >= y) {
l.bf 2f
l.sub r3, r3, r4 # x = xy;
1: l.sfltu r3, r4 # while (x >= y) {
l.bf 2f
l.sub r3, r3, r4 # x = xy;
l.add r4, r4, r4 # y *= 2;
l.j 1b # }
2: l.sfltu r12, r4 # } for (;;) {
l.bf 3f # if (r >= y) {
l.sfeq r4, r5 # [if (y == y0)]
l.sub r12, r12, r4 # r = ry;
l.addi r11, r11, 1 # q = q + 1;
3: l.bf 4f # } if (y == y0) break;
l.srli r4, r4, 1 # y >>= 1;
l.j 2b # }
l.add r11, r11, r11 # q *= 2;
4: l.jr r9 # return q;
l.nop
5: l.ori r11, r3, 0 # identity:
l.jr r9 # return x;
l.ori r12, r0, 0 # r = 0;
endfunc __udivsi3 # }
func __umodsi3
l.sw -4(r1), r9
l.jal __udivsi3
l.addi r1, r1, -4
l.addi r1, r1, 4
l.lwz r9, -4(r1)
l.jr r9
l.ori r11, r12, 0
endfunc __umodsi3

41
lib/ar100/start.S Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright © 2017-2020 The Crust Firmware Authors.
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only
*/
#include <macros.S>
#include <asm/spr.h>
func start
l.mfspr r2, r0, SPR_SYS_PPC_ADDR
l.sfltui r2, 0x4000 # Size of exception vector area
l.bf 1f
l.srli r2, r2, 8 # Vector address exception number
l.addi r2, r0, 0 # Set to zero if not an exception
1: l.addi r3, r0, 0 # Invalidate instruction cache
l.addi r4, r0, 4096 # Cache lines (256) * block size (16)
2: l.mtspr r0, r3, SPR_ICACHE_ICBIR_ADDR
l.sfltu r3, r4
l.bf 2b
l.addi r3, r3, 16 # Cache block size
l.psync # Flush CPU pipeline
l.mfspr r3, r0, SPR_SYS_SR_ADDR # Enable instruction cache
l.ori r3, r3, SPR_SYS_SR_ICE_MASK
l.mtspr r0, r3, SPR_SYS_SR_ADDR
l.nop # One cache block of nops
l.nop
l.nop
l.nop
l.movhi r3, hi(__bss_start) # Clear .bss
l.ori r3, r3, lo(__bss_start)
l.movhi r4, hi(__bss_end)
l.ori r4, r4, lo(__bss_end)
3: l.sw 0(r3), r0
l.sfltu r3, r4
l.bf 3b
l.addi r3, r3, 4
l.movhi r1, hi(__stack_end)
l.ori r1, r1, lo(__stack_end) # Initialize stack pointer
l.j main # Jump to C entry point
l.or r3, r2, r2
endfunc start

View File

@ -8,6 +8,7 @@ set -eu
MAIN_DIR=${PWD} MAIN_DIR=${PWD}
BUILD_DIR=${PWD}/ci_build BUILD_DIR=${PWD}/ci_build
export PATH=${BUILD_DIR}/pru-gcc/bin:${PATH} export PATH=${BUILD_DIR}/pru-gcc/bin:${PATH}
export PATH=${BUILD_DIR}/or1k-linux-musl-cross/bin:${PATH}
PYTHON=${BUILD_DIR}/python-env/bin/python PYTHON=${BUILD_DIR}/python-env/bin/python
PYTHON2=${BUILD_DIR}/python2-env/bin/python PYTHON2=${BUILD_DIR}/python2-env/bin/python

View File

@ -47,7 +47,21 @@ else
tar xfz ${PRU_FILE} tar xfz ${PRU_FILE}
fi fi
######################################################################
# Install or1k-linux-musl toolchain
######################################################################
echo -e "\n\n=============== Install or1k-linux-musl toolchain\n\n"
TOOLCHAIN=or1k-linux-musl-cross
TOOLCHAIN_ZIP=${TOOLCHAIN}.tgz
GCC_VERSION=10
TOOLCHAIN_ZIP_V=${TOOLCHAIN}-${GCC_VERSION}.tgz
URL=https://more.musl.cc/${GCC_VERSION}/x86_64-linux-musl/
if [ ! -f ${CACHE_DIR}/${TOOLCHAIN_ZIP_V} ]; then
curl ${URL}/${TOOLCHAIN_ZIP} -o ${CACHE_DIR}/${TOOLCHAIN_ZIP_V}
fi
cd ${BUILD_DIR}
tar xf ${CACHE_DIR}/${TOOLCHAIN_ZIP_V}
###################################################################### ######################################################################
# Create python3 virtualenv environment # Create python3 virtualenv environment
###################################################################### ######################################################################

102
scripts/flash-ar100.py Executable file
View File

@ -0,0 +1,102 @@
#!/usr/bin/env python3
# This file may be distributed under the terms of the GNU GPLv3 license.
import mmap
import argparse
import sys
FW_START = 0x00004000
FW_BASE = 0x00040000 + FW_START
FW_LIMIT = 0x00054000
FW_SIZE = FW_LIMIT - FW_BASE
EXCEPTIONS_BASE = 0x00040000
EXCEPTIONS_LIMIT = 0x00042000
EXCEPTIONS_SIZE = EXCEPTIONS_LIMIT - EXCEPTIONS_BASE
EXCEPTIONS_JUMP = FW_START # All exceptions reset program
NR_OF_EXCEPTIONS = 14
R_CPU_CFG_PAGE_BASE = 0x01F01000
R_CPU_CFG_PAGE_LIMIT = 0x01F02000
R_CPU_CFG_SIZE = R_CPU_CFG_PAGE_LIMIT - R_CPU_CFG_PAGE_BASE
R_CPU_CFG_OFFSET = 0xC00
R_CPU_CLK_OFFSET = 0x400
parser = argparse.ArgumentParser(description='Flash and reset SRAM A2 of A64')
parser.add_argument('filename', nargs='?', help='binary file to write')
parser.add_argument('--reset', action='store_true', help='reset the AR100')
parser.add_argument('--halt', action='store_true', help='Halt the AR100')
parser.add_argument('--bl31', action='store_true', help='write bl31')
args = parser.parse_args()
def write_exception_vectors():
print("Writing exception vectors")
with open("/dev/mem", "w+b") as f:
exc = mmap.mmap(f.fileno(),
length=EXCEPTIONS_SIZE,
offset=EXCEPTIONS_BASE)
for i in range(NR_OF_EXCEPTIONS):
add = i * 0x100
exc[add:add + 4] = ((EXCEPTIONS_JUMP - add) >> 2).to_bytes(
4, byteorder='little')
exc.close()
def assert_deassert_reset(ass):
with open("/dev/mem", "w+b") as f:
r_cpucfg = mmap.mmap(f.fileno(),
length=R_CPU_CFG_SIZE,
offset=R_CPU_CFG_PAGE_BASE)
if ass:
r_cpucfg[R_CPU_CFG_OFFSET] &= ~0x01
if r_cpucfg[R_CPU_CFG_OFFSET] & 0x01:
print("failed to assert reset")
else:
r_cpucfg[R_CPU_CFG_OFFSET] |= 0x01
if not (r_cpucfg[R_CPU_CFG_OFFSET] & 0x01):
print("failed to deassert reset")
r_cpucfg.close()
def write_file(filename):
with open(filename, "r+b") as fw:
data = fw.read()
if len(data) > FW_SIZE:
print("File does not fit in memory")
sys.exit(1)
print("Writing file to SRAM A2")
with open("/dev/mem", "w+b") as f:
sram_a2 = mmap.mmap(f.fileno(), length=FW_SIZE, offset=FW_BASE)
sram_a2[0:len(data)] = data
sram_a2.close()
def clear_magic_word():
with open("/dev/mem", "w+b") as f:
sram_a2 = mmap.mmap(f.fileno(), length=FW_SIZE, offset=FW_BASE)
sram_a2[0] = 0x0
sram_a2.close()
if args.reset:
print("Resetting AR100")
assert_deassert_reset(1)
assert_deassert_reset(0)
sys.exit(0)
if args.filename:
if args.bl31:
print("writing bl31")
assert_deassert_reset(1)
write_file(args.filename)
else:
assert_deassert_reset(1)
write_exception_vectors()
write_file(args.filename)
assert_deassert_reset(0)
if args.halt:
print("Halting AR100")
assert_deassert_reset(1)
clear_magic_word()

View File

@ -26,6 +26,8 @@ choice
bool "Raspberry Pi RP2040" bool "Raspberry Pi RP2040"
config MACH_PRU config MACH_PRU
bool "Beaglebone PRU" bool "Beaglebone PRU"
config MACH_AR100
bool "Allwinner A64 AR100"
config MACH_LINUX config MACH_LINUX
bool "Linux process" bool "Linux process"
config MACH_SIMU config MACH_SIMU
@ -40,6 +42,7 @@ source "src/stm32/Kconfig"
source "src/hc32f460/Kconfig" source "src/hc32f460/Kconfig"
source "src/rp2040/Kconfig" source "src/rp2040/Kconfig"
source "src/pru/Kconfig" source "src/pru/Kconfig"
source "src/ar100/Kconfig"
source "src/linux/Kconfig" source "src/linux/Kconfig"
source "src/simulator/Kconfig" source "src/simulator/Kconfig"

21
src/ar100/Kconfig Normal file
View File

@ -0,0 +1,21 @@
# Kconfig settings for AR100
if MACH_AR100
config AR100_SELECT
bool
default y
select HAVE_GPIO
select HAVE_GPIO_SPI
select HAVE_GPIO_BITBANGING
select HAVE_STEPPER_BOTH_EDGE
config BOARD_DIRECTORY
string
default "ar100"
config CLOCK_FREQ
int
default 300000000
endif

45
src/ar100/Makefile Normal file
View File

@ -0,0 +1,45 @@
CROSS_PREFIX=or1k-linux-musl-
dirs-y += src/generic src/ar100 lib/ar100
CFLAGS += -O3
CFLAGS += -fno-builtin
CFLAGS += -fno-pie
CFLAGS += -ffreestanding
CFLAGS += -msfimm -mshftimm -msoft-div -msoft-mul
CFLAGS += -Ilib/ar100
CFLAGS_klipper.elf := $(CFLAGS) -T src/ar100/ar100.ld
CFLAGS_klipper.elf += -Wl,--gc-sections -static
CFLAGS_klipper.elf += -Wl,--no-dynamic-linker
SFLAGS = -nostdinc -MMD
SFLAGS += -Ilib/ar100
# Add source files
src-y += ar100/main.c ar100/gpio.c ar100/serial.c
src-y += ar100/util.c ar100/timer.c
src-y += generic/crc16_ccitt.c generic/timer_irq.c
# Remove files that are not needed to save space
src-y := $(filter-out lcd_hd44780.c,$(src-y))
src-y := $(filter-out lcd_st7920.c,$(src-y))
src-y := $(filter-out sensor_angle.c,$(src-y))
src-y := $(filter-out thermocouple.c,$(src-y))
OBJS_klipper.elf += $(OUT)lib/ar100/start.o
OBJS_klipper.elf += $(OUT)lib/ar100/runtime.o
# Build the AR100 binary
target-y += $(OUT)ar100.bin
$(OUT)lib/ar100/start.o:
@echo " Compiling $@"
$(Q)$(CC) $(SFLAGS) -c $(PWD)/lib/ar100/start.S -o $@
$(OUT)lib/ar100/runtime.o:
@echo " Compiling $@"
$(Q)$(CC) $(SFLAGS) -c $(PWD)/lib/ar100/runtime.S -o $@
$(OUT)ar100.bin: $(OUT)klipper.elf
@echo " Object copy $@"
$(OBJCOPY) -O binary -S --reverse-bytes 4 $(OUT)klipper.elf $@
truncate -s %8 $@

58
src/ar100/ar100.ld Normal file
View File

@ -0,0 +1,58 @@
OUTPUT_ARCH(or1k)
OUTPUT_FORMAT(elf32-or1k)
ENTRY (start)
STACK_SIZE = 0x200;
SRAM_A2_SIZE = 64K;
ORIG = 0x4000;
MEMORY {
SRAM_A2 (rwx): ORIGIN = ORIG, LENGTH = SRAM_A2_SIZE
}
SECTIONS
{
. = ORIG;
.text . : ALIGN(4) {
KEEP(*(.text.start))
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.text*)))
. = ALIGN(4);
} >SRAM_A2
.data . : ALIGN(4) {
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
__data_start = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.data*)))
. = ALIGN(4);
__data_end = .;
} >SRAM_A2
.copy . : ALIGN(4) {
__copy_start = .;
. += __data_end - __data_start;
__copy_end = .;
. = ALIGN(4);
} >SRAM_A2
.bss . : ALIGN(4) {
__bss_start = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
. = ALIGN(4);
__bss_end = .;
__stack_start = .;
. += STACK_SIZE;
__stack_end = .;
} >SRAM_A2
ASSERT(. <= (SRAM_A2_SIZE), "Klipper image is too large")
/DISCARD/ : {
*(.comment*)
*(.eh_frame_hdr*)
*(.iplt*)
*(.note*)
*(.rela*)
*( .compile_time_request )
}
}

127
src/ar100/gpio.c Normal file
View File

@ -0,0 +1,127 @@
// GPIO functions on ar100
//
// Copyright (C) 2020-2021 Elias Bakken <elias@iagent.no>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "gpio.h"
#include "command.h"
#include "internal.h"
#include "util.h"
DECL_ENUMERATION_RANGE("pin", "PL0", 0*32, 13);
DECL_ENUMERATION_RANGE("pin", "PB0", 1*32, 10);
DECL_ENUMERATION_RANGE("pin", "PC0", 2*32, 17);
DECL_ENUMERATION_RANGE("pin", "PD0", 3*32, 25);
DECL_ENUMERATION_RANGE("pin", "PE0", 4*32, 18);
DECL_ENUMERATION_RANGE("pin", "PF0", 5*32, 7);
DECL_ENUMERATION_RANGE("pin", "PG0", 6*32, 14);
DECL_ENUMERATION_RANGE("pin", "PH0", 7*32, 12);
#define BANK(x) (x/32)
#define PIN(x) (x%32)
#define CFG_REG(x) ((x/8)*4)
#define CFG_OFF(x) ((x%8)*4)
#define PULLUP_REG(x) 0x1C + ((x/16)*4)
#define PULLUP_OFF(x) ((x%16)*2)
volatile uint32_t data_regs[8];
struct gpio_mux gpio_mux_setup(uint8_t pin, enum pin_func func){
uint8_t bank = BANK(pin);
uint8_t p = PIN(pin);
uint32_t data_reg = PIO_BASE + bank*0x24 + 0x10;
uint32_t cfg_reg = PIO_BASE + bank*0x24 + CFG_REG(p);
uint8_t cfg_off = CFG_OFF(p);
if(bank == 0) { // Handle R_PIO
data_reg = R_PIO_BASE + 0x10;
cfg_reg = R_PIO_BASE + CFG_REG(p);
}
uint32_t curr_val = read_reg(cfg_reg) & ~(0xF<<cfg_off);
write_reg(cfg_reg, curr_val | func<<cfg_off);
struct gpio_mux ret = {
.pin = p,
.reg = data_reg,
.bank = bank
};
return ret;
}
struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val){
struct gpio_mux mux = gpio_mux_setup(pin, PIO_OUTPUT);
struct gpio_out ret = {
.pin = mux.pin,
.reg = mux.reg,
.bank = mux.bank,
};
data_regs[ret.bank] = read_reg(ret.reg);
gpio_out_write(ret, val);
return ret;
}
void gpio_out_write(struct gpio_out pin, uint8_t val){
data_regs[pin.bank] &= ~(1<<pin.pin);
data_regs[pin.bank] |= ((!!val)<<pin.pin);
write_reg(pin.reg, data_regs[pin.bank]);
}
void gpio_out_reset(struct gpio_out pin, uint8_t val){
uint8_t p = pin.bank * 32 + pin.pin;
gpio_out_setup(p, val);
}
uint8_t gpio_in_read(struct gpio_in pin){
data_regs[pin.bank] = read_reg(pin.reg);
return !!(data_regs[pin.bank] & (1<<pin.pin));
}
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up){
struct gpio_mux mux = gpio_mux_setup(pin, PIO_INPUT);
uint32_t pullup_reg = PIO_BASE + mux.bank*0x24 + PULLUP_REG(mux.pin);
uint8_t pullup_off = PULLUP_OFF(mux.pin);
if(mux.bank == 0) { // Handle R_PIO
pullup_reg = R_PIO_BASE + PULLUP_REG(mux.pin);
}
write_reg(pullup_reg, pull_up<<pullup_off);
struct gpio_in in = {
.pin = mux.pin,
.reg = mux.reg,
.bank = mux.bank
};
data_regs[mux.bank] = read_reg(mux.reg);
return in;
}
void gpio_in_reset(struct gpio_in pin, int8_t pull_up){
uint8_t p = pin.bank * 32 + pin.pin;
gpio_in_setup(p, pull_up);
}
void gpio_out_toggle_noirq(struct gpio_out pin){
data_regs[pin.bank] ^= (1<<pin.pin);
*((volatile uint32_t *)(pin.reg)) = data_regs[pin.bank];
}
void gpio_out_toggle(struct gpio_out pin){
gpio_out_toggle_noirq(pin);
}
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate){
return (struct spi_config){ };
}
void spi_prepare(struct spi_config config){
}
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data){
}

43
src/ar100/gpio.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef __AR100_GPIO_H
#define __AR100_GPIO_H
#include <stdint.h>
struct gpio_out {
uint8_t pin;
uint8_t bank;
uint32_t reg;
};
struct gpio_in {
uint8_t pin;
uint8_t bank;
uint32_t reg;
};
extern volatile uint32_t data_regs[8];
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
void gpio_in_reset(struct gpio_in pin, int8_t pull_up);
uint8_t gpio_in_read(struct gpio_in pin);
struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val);
void gpio_out_write(struct gpio_out pin, uint8_t val);
void gpio_out_reset(struct gpio_out pin, uint8_t val);
void gpio_out_toggle_noirq(struct gpio_out pin);
void gpio_out_toggle(struct gpio_out pin);
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
void gpio_in_reset(struct gpio_in pin, int8_t pull_up);
uint8_t gpio_in_read(struct gpio_in pin);
struct spi_config {
void *spi;
uint32_t spi_cr1;
};
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
void spi_prepare(struct spi_config config);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
#endif

34
src/ar100/internal.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __AR100_INTERNAL_H
#define __AR100_INTERNAL_H
#define R_PIO_BASE 0x01F02C00
#define PIO_BASE 0x01C20800
enum pin_func {
PIO_INPUT,
PIO_OUTPUT,
PIO_ALT1,
PIO_ALT2,
PIO_ALT3,
PIO_ALT4,
PIO_ALT5,
PIO_DISABLE
};
struct gpio_mux {
uint32_t pin;
uint8_t bank;
uint32_t reg;
};
struct gpio_mux gpio_mux_setup(uint8_t pin, enum pin_func func);
static inline unsigned long mfspr(unsigned long add){
unsigned long ret;
__asm__ __volatile__ ("l.mfspr %0,r0,%1" : "=r" (ret) : "K" (add));
return ret;
}
static inline void mtspr(unsigned long add, unsigned long val){
__asm__ __volatile__ ("l.mtspr r0,%1,%0" : : "K" (add), "r" (val));
}
#endif // internal.h

157
src/ar100/main.c Normal file
View File

@ -0,0 +1,157 @@
// Main entry point for ar100
//
// Copyright (C) 2020-2021 Elias Bakken <elias@iagent.no>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <stdint.h> // uint32_t
#include <string.h>
#include "board/misc.h" // dynmem_start
#include "board/irq.h" // irq_disable
#include "command.h" // shutdown
#include "generic/timer_irq.h" // timer_dispatch_many
#include "sched.h" // sched_main
#include "asm/spr.h"
#include "util.h"
#include "gpio.h"
#include "serial.h"
#include "timer.h"
DECL_CONSTANT_STR("MCU", "ar100");
#define RESET_VECTOR 0x0100
static struct task_wake console_wake;
static uint8_t receive_buf[192];
static int receive_pos;
static char dynmem_pool[8 * 1024];
void *
dynmem_start(void)
{
return dynmem_pool;
}
void *
dynmem_end(void)
{
return &dynmem_pool[sizeof(dynmem_pool)];
}
void
irq_disable(void)
{
}
void
irq_enable(void)
{
}
irqstatus_t
irq_save(void)
{
return 0;
}
void
irq_restore(irqstatus_t flag)
{
}
void
irq_wait(void)
{
irq_poll();
}
void
irq_poll(void)
{
if(timer_interrupt_pending()) {
timer_clear_interrupt();
uint32_t next = timer_dispatch_many();
timer_set(next);
}
if(r_uart_fifo_rcv())
sched_wake_task(&console_wake);
}
/****************************************************************
* Console IO
****************************************************************/
// Process any incoming commands
void
console_task(void)
{
if (!sched_check_wake(&console_wake))
return;
int ret = 0;
for(int i=0; i<r_uart_fifo_rcv(); i++) {
receive_buf[receive_pos + ret++] = r_uart_getc();
}
if(!ret)
return;
int len = receive_pos + ret;
uint_fast8_t pop_count, msglen = len > MESSAGE_MAX ? MESSAGE_MAX : len;
ret = command_find_and_dispatch(receive_buf, msglen, &pop_count);
if (ret) {
len -= pop_count;
if (len) {
memcpy(receive_buf, &receive_buf[pop_count], len);
sched_wake_task(&console_wake);
}
}
receive_pos = len;
}
DECL_TASK(console_task);
// Encode and transmit a "response" message
void
console_sendf(const struct command_encoder *ce, va_list args)
{
uint8_t buf[MESSAGE_MAX];
uint_fast8_t msglen = command_encode_and_frame(buf, ce, args);
for(int i=0; i<msglen; i++) {
r_uart_putc(buf[i]);
}
}
void restore_data(void)
{
extern char __data_start, __data_end, __copy_start;
memcpy (&__data_start, &__copy_start, &__data_end - &__data_start);
}
void
command_reset(uint32_t *args)
{
timer_reset();
restore_data();
void *reset = (void *)RESET_VECTOR;
goto *reset;
}
DECL_COMMAND_FLAGS(command_reset, HF_IN_SHUTDOWN, "reset");
void
save_data(void)
{
extern char __data_start, __data_end, __copy_start;
memcpy (&__copy_start, &__data_start, &__data_end - &__data_start);
}
__noreturn void
main(uint32_t exception);
__noreturn void
main(uint32_t exception)
{
save_data();
r_uart_init();
sched_main();
while(1) {} // Stop complaining about noreturn
}

53
src/ar100/serial.c Normal file
View File

@ -0,0 +1,53 @@
// Uart and r_uart functions for ar100
//
// Copyright (C) 2020-2021 Elias Bakken <elias@iagent.no>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "serial.h"
#include "util.h"
#include "internal.h"
#include "gpio.h"
void r_uart_init(void){
// Setup Pins PL2, PL3 as UART IO
gpio_mux_setup(2, PIO_ALT1);
gpio_mux_setup(3, PIO_ALT1);
// Enable clock and assert reset
clear_bit(APB0_CLK_GATING_REG, 4);
set_bit(APB0_SOFT_RST_REG, 4);
set_bit(APB0_CLK_GATING_REG, 4);
// Setup baud rate
set_bit(R_UART_LCR, 7); // Enable setting DLH, DLL
write_reg(R_UART_DLH, 0x0);
write_reg(R_UART_DLL, 0xD); // 1 500 000
write_reg(R_UART_LCR, 0x3); // 8 bit data length
write_reg(R_UART_FCR, 0<<0); // Disable fifo
r_uart_getc(); // flush input
write_reg(R_UART_FCR, 1<<0); // Enable fifo
}
char r_uart_getc(void){
char c = (char) read_reg(R_UART_RBR);
return c;
}
uint32_t r_uart_fifo_rcv(void){
return read_reg(R_UART_RFL);
}
void r_uart_putc(char c){
while(!(read_reg(R_UART_LSR) & 1<<5))
;
write_reg(R_UART_THR, c);
}
void r_uart_puts(char *s){
while(*s){
r_uart_putc(*s++);
}
}

50
src/ar100/serial.h Normal file
View File

@ -0,0 +1,50 @@
#include <stdint.h>
#define R_UART_BASE 0x01F02800
#define R_UART_RBR R_UART_BASE + 0x00 // UART Receive Buffer Register
#define R_UART_THR R_UART_BASE + 0x00 // UART Transmit Holding Register
#define R_UART_DLL R_UART_BASE + 0x00 // UART Divisor Latch Low Register
#define R_UART_DLH R_UART_BASE + 0x04 // UART Divisor Latch High Register
#define R_UART_IER R_UART_BASE + 0x04 // UART Interrupt Enable Register
#define R_UART_IIR R_UART_BASE + 0x08 // UART Interrupt Identity Register
#define R_UART_FCR R_UART_BASE + 0x08 // UART FIFO Control Register
#define R_UART_LCR R_UART_BASE + 0x0C // UART Line Control Register
#define R_UART_MCR R_UART_BASE + 0x10 // UART Modem Control Register
#define R_UART_LSR R_UART_BASE + 0x14 // UART Line Status Register
#define R_UART_MSR R_UART_BASE + 0x18 // UART Modem Status Register
#define R_UART_SCH R_UART_BASE + 0x1C // UART Scratch Register
#define R_UART_USR R_UART_BASE + 0x7C // UART Status Register
#define R_UART_TFL R_UART_BASE + 0x80 // UART Transmit FIFO Level
#define R_UART_RFL R_UART_BASE + 0x84 // UART_RFL
#define R_UART_HLT R_UART_BASE + 0xA4 // UART Halt TX Register
#define UART0_BASE 0x01C28000
#define UART0_RBR UART0_BASE + 0x00 // UART Receive Buffer Register
#define UART0_THR UART0_BASE + 0x00 // UART Transmit Holding Register
#define UART0_DLL UART0_BASE + 0x00 // UART Divisor Latch Low Register
#define UART0_DLH UART0_BASE + 0x04 // UART Divisor Latch High Register
#define UART0_IER UART0_BASE + 0x04 // UART Interrupt Enable Register
#define UART0_IIR UART0_BASE + 0x08 // UART Interrupt Identity Register
#define UART0_FCR UART0_BASE + 0x08 // UART FIFO Control Register
#define UART0_LCR UART0_BASE + 0x0C // UART Line Control Register
#define UART0_MCR UART0_BASE + 0x10 // UART Modem Control Register
#define UART0_LSR UART0_BASE + 0x14 // UART Line Status Register
#define UART0_MSR UART0_BASE + 0x18 // UART Modem Status Register
#define UART0_SCH UART0_BASE + 0x1C // UART Scratch Register
#define UART0_USR UART0_BASE + 0x7C // UART Status Register
#define UART0_TFL UART0_BASE + 0x80 // UART Transmit FIFO Level
#define UART0_RFL UART0_BASE + 0x84 // UART_RFL
#define UART0_HLT UART0_BASE + 0xA4 // UART Halt TX Register
#define R_PRCM_BASE 0x01F01400
#define APB0_CLK_GATING_REG R_PRCM_BASE + 0x0028 // APB0 Clock Gating Reg
#define APB0_SOFT_RST_REG R_PRCM_BASE + 0x00B0 // APB0 SW Reset Reg
void r_uart_init(void);
void r_uart_putc(char c);
char r_uart_getc(void);
uint32_t r_uart_fifo_rcv(void);
void uart_putc(char c);
void uart_puts(char *s);
void uart_puti(uint32_t u);

52
src/ar100/timer.c Normal file
View File

@ -0,0 +1,52 @@
// Timer functions for ar100
//
// Copyright (C) 2020-2021 Elias Bakken <elias@iagent.no>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "timer.h"
#include "board/timer_irq.h"
#include "board/misc.h"
volatile static uint32_t timer_compare;
static uint8_t interrupt_seen;
uint8_t timer_interrupt_pending(void){
if(interrupt_seen){
return 0;
}
if(timer_is_before(mfspr(SPR_TICK_TTCR_ADDR), timer_compare)){
return 0;
}
return 1;
}
void timer_clear_interrupt(void){
interrupt_seen = 1;
}
// Set the next timer wake up time
void timer_set(uint32_t value){
timer_compare = value;
interrupt_seen = 0;
}
// Return the current time (in absolute clock ticks).
uint32_t timer_read_time(void){
return mfspr(SPR_TICK_TTCR_ADDR);
}
void timer_reset(void){
mtspr(SPR_TICK_TTCR_ADDR, 0);
}
// Activate timer dispatch as soon as possible
void timer_kick(void){
timer_set(timer_read_time() + 50);
}
void timer_init(void){
interrupt_seen = 1;
mtspr(SPR_TICK_TTMR_ADDR, 3<<30); // continous
timer_kick();
}
DECL_INIT(timer_init);

12
src/ar100/timer.h Normal file
View File

@ -0,0 +1,12 @@
#include <stdint.h>
#include "asm/spr.h"
#include "sched.h"
#include "internal.h"
uint8_t timer_interrupt_pending(void);
void timer_set(uint32_t value);
uint32_t timer_read_time(void);
void timer_reset(void);
void timer_clear_interrupt(void);
void timer_kick(void);
void timer_init(void);

34
src/ar100/util.c Normal file
View File

@ -0,0 +1,34 @@
// Helper functions for ar100
//
// Copyright (C) 2020-2021 Elias Bakken <elias@iagent.no>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "util.h"
void *memcpy(void *restrict dest, const void *restrict src, size_t n){
// Typecast src and dest addresses to (char *)
char *csrc = (char *)src;
char *cdest = (char *)dest;
// Copy contents of src[] to dest[]
for (int i=0; i<n; i++)
cdest[i] = csrc[i];
return dest;
}
void *memset(void *dest, int c, size_t n){
unsigned char *s = dest;
for(; n; n--){
*s++ = c;
}
return dest;
}
void set_bit(uint32_t addr, uint8_t bit){
write_reg(addr, read_reg(addr) | (1<<bit));
}
void clear_bit(uint32_t addr, uint8_t bit){
write_reg(addr, read_reg(addr) & ~(1<<bit));
}

17
src/ar100/util.h Normal file
View File

@ -0,0 +1,17 @@
#include <stdint.h>
#include <stddef.h>
void *memcpy(void *restrict dest, const void *restrict src, size_t n);
void *memset(void *restrict dest, int c, size_t n);
inline void write_reg(uint32_t addr, uint32_t val){
*((volatile unsigned long *)(addr)) = val;
}
inline uint32_t read_reg(uint32_t addr){
return *((volatile unsigned long *)(addr));
}
void set_bit(uint32_t addr, uint8_t bit);
void clear_bit(uint32_t addr, uint8_t bit);

View File

@ -0,0 +1,2 @@
# Base config file for the ar100 CPU
CONFIG_MACH_AR100=y