Compare commits
16 Commits
Author | SHA1 | Date |
---|---|---|
Sean Cross | fc9acfbb65 | |
Sean Cross | 8e7b41f01a | |
Sean Cross | e98aac8fde | |
Sean Cross | 3bcd5ee328 | |
Sean Cross | 2a3c5d352e | |
Sean Cross | 165cf24bc7 | |
Sean Cross | 18178e4a71 | |
Sean Cross | 337e1a370c | |
Sean Cross | 946c7f8ec1 | |
Sean Cross | 372ab22b10 | |
Sean Cross | 8b2241a9e6 | |
Sean Cross | 6d8875850c | |
Sean Cross | 5ff6153b53 | |
Sean Cross | 0c8f29c7bf | |
Sean Cross | 8e811ff03e | |
Sean Cross | d728421d0c |
|
@ -1 +1 @@
|
|||
Subproject commit 3052a16d9f18360f407ce7a4d0cbd82e24cb4768
|
||||
Subproject commit 7f1ce91cbb27bfb26b214f5a810ef67d6f18ac69
|
|
@ -328,26 +328,139 @@ class Platform(LatticePlatform):
|
|||
raise ValueError("programming is not supported")
|
||||
|
||||
class SBLED(Module, AutoCSR):
|
||||
def __init__(self, pads):
|
||||
def __init__(self, pads, detected_pulse):
|
||||
rgba_pwm = Signal(3)
|
||||
|
||||
self.dat = CSRStorage(8)
|
||||
self.addr = CSRStorage(4)
|
||||
self.ctrl = CSRStorage(4)
|
||||
|
||||
self.bypass = CSRStorage(3)
|
||||
self.pulse = CSRStorage(24)
|
||||
self.duty = CSRStorage(24)
|
||||
self.sent_pulses = CSRStatus(32)
|
||||
self.detected_pulses = CSRStatus(32)
|
||||
|
||||
count = Signal(24)
|
||||
led_value = Signal()
|
||||
rgb = Signal(3)
|
||||
sent_pulses = Signal(32)
|
||||
detected_pulses = Signal(32)
|
||||
rgba_drv = Signal(3)
|
||||
|
||||
last_detected_val = Signal()
|
||||
|
||||
self.sync += [
|
||||
If(last_detected_val != detected_pulse,
|
||||
detected_pulses.eq(detected_pulses + 1),
|
||||
),
|
||||
last_detected_val.eq(detected_pulse),
|
||||
# When the PWM count is updated, reset everything and
|
||||
# copy the results out.
|
||||
If(self.pulse.re,
|
||||
count.eq(0),
|
||||
|
||||
self.sent_pulses.status.eq(sent_pulses),
|
||||
sent_pulses.eq(0),
|
||||
|
||||
self.detected_pulses.status.eq(detected_pulses),
|
||||
detected_pulses.eq(0),
|
||||
|
||||
led_value.eq(0),
|
||||
|
||||
).Elif(count < self.pulse.storage,
|
||||
count.eq(count + 1),
|
||||
If(count < self.duty.storage,
|
||||
led_value.eq(0),
|
||||
).Elif(count == self.duty.storage,
|
||||
led_value.eq(1),
|
||||
sent_pulses.eq(sent_pulses + 1),
|
||||
).Else(
|
||||
led_value.eq(1),
|
||||
),
|
||||
).Else(
|
||||
# Reset the count once it gets greater than "Pulse"
|
||||
count.eq(0),
|
||||
led_value.eq(0),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
# last_led_value = Signal()
|
||||
# self.sync += [
|
||||
# last_led_value.eq(led_value),
|
||||
# # When the PWM count is updated, reset everything and
|
||||
# # copy the results out.
|
||||
# If(self.pulse.re,
|
||||
# count.eq(0),
|
||||
|
||||
# self.sent_pulses.status.eq(sent_pulses),
|
||||
# sent_pulses.eq(0),
|
||||
|
||||
# self.detected_pulses.status.eq(detected_pulses),
|
||||
# detected_pulses.eq(0),
|
||||
|
||||
# led_value.eq(0),
|
||||
|
||||
# ).Elif(count < self.pulse.storage,
|
||||
# count.eq(count + 1),
|
||||
# If(count < self.duty.storage,
|
||||
# led_value.eq(0),
|
||||
# ).Else(
|
||||
# led_value.eq(1),
|
||||
|
||||
# # On the transition from 0 > 1, increment the counter
|
||||
# # and see if the LED has changed. If so, increment
|
||||
# # the number of detected pulses.
|
||||
# If(~last_led_value,
|
||||
# sent_pulses.eq(sent_pulses + 1),
|
||||
# If(detected_pulse,
|
||||
# detected_pulses.eq(detected_pulses + 1),
|
||||
# ),
|
||||
# ),
|
||||
# ),
|
||||
# ).Else(
|
||||
# # Reset the count once it gets greater than "Pulse"
|
||||
# count.eq(0),
|
||||
# led_value.eq(0),
|
||||
# ),
|
||||
# ]
|
||||
|
||||
# Wire up the bypasses
|
||||
self.comb += [
|
||||
If(self.bypass.storage[0],
|
||||
rgb[0].eq(led_value),
|
||||
).Else(
|
||||
rgb[0].eq(rgba_pwm[0]),
|
||||
),
|
||||
If(self.bypass.storage[1],
|
||||
rgb[1].eq(led_value),
|
||||
).Else(
|
||||
rgb[1].eq(rgba_pwm[1]),
|
||||
),
|
||||
If(self.bypass.storage[2],
|
||||
rgb[2].eq(led_value),
|
||||
).Else(
|
||||
rgb[2].eq(rgba_pwm[2]),
|
||||
),
|
||||
]
|
||||
|
||||
self.specials += Instance("SB_RGBA_DRV",
|
||||
i_CURREN = self.ctrl.storage[1],
|
||||
i_RGBLEDEN = self.ctrl.storage[2],
|
||||
i_RGB0PWM = rgba_pwm[0],
|
||||
i_RGB1PWM = rgba_pwm[1],
|
||||
i_RGB2PWM = rgba_pwm[2],
|
||||
i_RGB0PWM = rgb[0],
|
||||
i_RGB1PWM = rgb[1],
|
||||
i_RGB2PWM = rgb[2],
|
||||
o_RGB0 = pads.rgb0,
|
||||
o_RGB1 = pads.rgb1,
|
||||
o_RGB2 = pads.rgb2,
|
||||
p_CURRENT_MODE = "0b1",
|
||||
p_RGB0_CURRENT = "0b000011",
|
||||
p_RGB1_CURRENT = "0b000001",
|
||||
p_RGB2_CURRENT = "0b000011",
|
||||
p_CURRENT_MODE = "0b1", # Half current
|
||||
# p_RGB0_CURRENT = "0b111111", # 12 mA
|
||||
# p_RGB1_CURRENT = "0b111111", # 12 mA
|
||||
# p_RGB2_CURRENT = "0b111111", # 12 mA
|
||||
p_RGB0_CURRENT = "0b000011", # 4 mA
|
||||
p_RGB1_CURRENT = "0b000011", # 4 mA
|
||||
p_RGB2_CURRENT = "0b000011", # 4 mA
|
||||
)
|
||||
|
||||
self.specials += Instance("SB_LEDDA_IP",
|
||||
|
@ -367,7 +480,6 @@ class SBLED(Module, AutoCSR):
|
|||
i_LEDDADDR0 = self.addr.storage[0],
|
||||
i_LEDDDEN = self.dat.re,
|
||||
i_LEDDEXE = self.ctrl.storage[0],
|
||||
# o_LEDDON = led_is_on, # Indicates whether LED is on or not
|
||||
# i_LEDDRST = ResetSignal(), # This port doesn't actually exist
|
||||
o_PWMOUT0 = rgba_pwm[0],
|
||||
o_PWMOUT1 = rgba_pwm[1],
|
||||
|
@ -375,7 +487,6 @@ class SBLED(Module, AutoCSR):
|
|||
o_LEDDON = Signal(),
|
||||
)
|
||||
|
||||
|
||||
class SBWarmBoot(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.ctrl = CSRStorage(size=8)
|
||||
|
@ -766,9 +877,6 @@ class BaseSoC(SoCCore):
|
|||
i_externalResetVector=self.reboot.addr.storage,
|
||||
)
|
||||
|
||||
self.submodules.rgb = SBLED(platform.request("led"))
|
||||
self.submodules.version = Version()
|
||||
|
||||
# Add USB pads
|
||||
usb_pads = platform.request("usb")
|
||||
usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
|
||||
|
@ -783,6 +891,8 @@ class BaseSoC(SoCCore):
|
|||
|
||||
# Add GPIO pads for the touch buttons
|
||||
self.submodules.touch = TouchPads(platform.request("touch"))
|
||||
self.submodules.rgb = SBLED(platform.request("led"), self.touch.i.status[1])
|
||||
self.submodules.version = Version()
|
||||
|
||||
# Add "-relut -dffe_min_ce_use 4" to the synth_ice40 command.
|
||||
# The "-reult" adds an additional LUT pass to pack more stuff in,
|
||||
|
@ -850,7 +960,7 @@ def main():
|
|||
cpu_type = "vexriscv"
|
||||
cpu_variant = "min"
|
||||
if args.with_debug:
|
||||
cpu_variant = "debug"
|
||||
cpu_variant = cpu_variant + "+debug"
|
||||
|
||||
if args.no_cpu:
|
||||
cpu_type = None
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
Install wiringpi to get the `gpio` command:
|
||||
|
||||
```
|
||||
apt install wiringpi
|
||||
```
|
||||
|
||||
There are five LEDs on the front. The indicate which subsystems work.
|
||||
|
||||
1: An error occurred
|
||||
2: Test Error
|
||||
3: Test Error
|
||||
4: Test Error
|
||||
5: Tester is IDLE (also, tester is READY)
|
||||
|
||||
LEDs 2, 3, and 4 form a pattern that indicate which test failed:
|
||||
|
||||
Test name | 2 (Green) | 3 (Blue) | 4 (White) | Reason for Failure |
|
||||
====================+===========+===========+===========+====================+
|
||||
SPI Valid | | x | x | The SPI flash was not found, or was not the correct model |
|
||||
USB Communication | x | | | The FPGA isn't assembled correctly, the USB pullup isn't correct, or the crystal is bad |
|
||||
Load Test Bitstream | | | | The FPGA isn't assembled correctly |
|
||||
Test SPI | | x | | Not all four wires are connected between the FPGA and the SPI |
|
||||
Test RGB | x | x | | TOUCH2 or the RGB LED are not assembled correctly |
|
||||
Test Touch | | x | x | TOUCH1, TOUCH3, or TOUCH4 are not assembled correctly |
|
||||
Final Bitstream | | | x | The final program could not be loaded |
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Remove any existing serial ports
|
||||
#if [ -e /dev/ttyACM* ]
|
||||
#then
|
||||
# rm -f /dev/ttyACM*
|
||||
#fi
|
||||
|
||||
fomu-flash -f ~/tester/bin/pvt-test-bitstream.bin 2> /dev/null
|
||||
|
||||
# Wait for serial port to appear
|
||||
while [ ! -e /dev/ttyACM* ]
|
||||
do
|
||||
true
|
||||
done
|
||||
|
||||
sleep .1
|
||||
stty -F /dev/ttyACM0 raw
|
||||
timeout 5 perl -e 'while (<>) { print; last if /^FOMU:/; }' < /dev/ttyACM0
|
Binary file not shown.
|
@ -0,0 +1,103 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <poll.h>
|
||||
#include "rpi.h"
|
||||
|
||||
#define GPIO 26
|
||||
|
||||
static int write_file(const char *filename, const char *string) {
|
||||
int fd = open(filename, O_WRONLY);
|
||||
if (fd == -1)
|
||||
return fd;
|
||||
|
||||
write(fd, string, strlen(string));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int export_gpio(int gpio) {
|
||||
char filename[512];
|
||||
char string[128];
|
||||
|
||||
snprintf(filename, sizeof(filename)-1, "/sys/class/gpio/export");
|
||||
snprintf(string, sizeof(string)-1, "%d", gpio);
|
||||
if (write_file(filename, string))
|
||||
return -1;
|
||||
|
||||
snprintf(filename, sizeof(filename)-1, "/sys/class/gpio/gpio%d/direction", gpio);
|
||||
snprintf(string, sizeof(string)-1, "in");
|
||||
if (write_file(filename, string))
|
||||
return -1;
|
||||
|
||||
snprintf(filename, sizeof(filename)-1, "/sys/class/gpio/gpio%d/edge", gpio);
|
||||
snprintf(string, sizeof(string)-1, "falling");
|
||||
if (write_file(filename, string))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char str[256];
|
||||
struct pollfd pfd;
|
||||
int fd, gpio;
|
||||
char buf[8];
|
||||
|
||||
if (argc > 1)
|
||||
gpio = atoi(argv[1]);
|
||||
else
|
||||
gpio = GPIO;
|
||||
|
||||
if (gpioInitialise()) {
|
||||
fprintf(stderr, "Unable to initialize GPIO! Are you running as root?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
gpioSetPullUpDown(gpio, PI_PUD_UP);
|
||||
|
||||
if (export_gpio(gpio)) {
|
||||
perror("Unable to export GPIO");
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(str, sizeof(str)-1, "/sys/class/gpio/gpio%d/value", gpio);
|
||||
|
||||
if ((fd = open(str, O_RDONLY)) < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed, gpio %d not exported.\n", gpio);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pfd.fd = fd;
|
||||
|
||||
pfd.events = POLLPRI;
|
||||
|
||||
lseek(fd, 0, SEEK_SET); /* consume any prior interrupt */
|
||||
read(fd, buf, sizeof buf);
|
||||
|
||||
while (1) {
|
||||
poll(&pfd, 1, -1); /* wait for interrupt */
|
||||
|
||||
lseek(fd, 0, SEEK_SET); /* consume interrupt */
|
||||
read(fd, buf, sizeof buf);
|
||||
//fprintf(stderr, "Delay...");
|
||||
usleep(1000);
|
||||
|
||||
/* Read value after brief debounce */
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
//fprintf(stderr, "Re-read...");
|
||||
read(fd, buf, sizeof buf);
|
||||
if (buf[0] != '0')
|
||||
continue;
|
||||
printf("START\n");
|
||||
fflush(stdout);
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#!/bin/sh
|
||||
|
||||
led_1=19
|
||||
led_2=13
|
||||
led_3=6
|
||||
led_4=5
|
||||
led_5=0
|
||||
|
||||
all_off() {
|
||||
gpio -g write ${led_1} 0
|
||||
gpio -g write ${led_2} 0
|
||||
gpio -g write ${led_3} 0
|
||||
gpio -g write ${led_4} 0
|
||||
gpio -g write ${led_5} 0
|
||||
}
|
||||
|
||||
green_on() {
|
||||
all_off
|
||||
gpio -g write ${green_led} 1
|
||||
}
|
||||
|
||||
yellow_on() {
|
||||
all_off
|
||||
gpio -g write ${yellow_led} 1
|
||||
}
|
||||
|
||||
red_on() {
|
||||
all_off
|
||||
gpio -g write ${red_led} 1
|
||||
}
|
||||
|
||||
red_also_on() {
|
||||
gpio -g write ${red_led} 1
|
||||
}
|
||||
|
||||
led_on() {
|
||||
case $1 in
|
||||
1) gpio -g write ${led_1} on ;;
|
||||
2) gpio -g write ${led_2} on ;;
|
||||
3) gpio -g write ${led_3} on ;;
|
||||
4) gpio -g write ${led_4} on ;;
|
||||
5) gpio -g write ${led_5} on ;;
|
||||
esac
|
||||
}
|
||||
|
||||
led_off() {
|
||||
case $1 in
|
||||
1) gpio -g write ${led_1} off ;;
|
||||
2) gpio -g write ${led_2} off ;;
|
||||
3) gpio -g write ${led_3} off ;;
|
||||
4) gpio -g write ${led_4} off ;;
|
||||
5) gpio -g write ${led_5} off ;;
|
||||
esac
|
||||
}
|
||||
|
||||
gpio_setup() {
|
||||
gpio -g mode ${led_1} out
|
||||
gpio -g mode ${led_2} out
|
||||
gpio -g mode ${led_3} out
|
||||
gpio -g mode ${led_4} out
|
||||
gpio -g mode ${led_5} out
|
||||
all_off
|
||||
led_on 5
|
||||
}
|
||||
|
||||
fail_test() {
|
||||
led_off 2
|
||||
led_off 3
|
||||
led_off 4
|
||||
|
||||
case $1 in
|
||||
load-tester-bitstream) ;;
|
||||
run-all-tests) led_on 2 ;;
|
||||
validate-spi) led_on 3 ; led_on 4 ;;
|
||||
test-spi) led_on 3 ;;
|
||||
test-rgbg) led_on 2 ; led_on 3 ;;
|
||||
test-rgbb) led_on 2 ; led_on 3 ;;
|
||||
test-rgbr) led_on 2 ; led_on 3 ;;
|
||||
test-touch) led_on 3 ; led_on 4 ;;
|
||||
load-final-bitstream) led_on 4 ;;
|
||||
verify-final-bitstream) led_on 4 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
gpio_setup
|
||||
|
||||
echo "HELLO bash-ltc-jig 1.0"
|
||||
while read line
|
||||
do
|
||||
if echo "${line}" | grep -iq '^start'
|
||||
then
|
||||
all_off
|
||||
led_on 2
|
||||
led_on 3
|
||||
led_on 4
|
||||
failures=0
|
||||
elif echo "${line}" | grep -iq '^fail'
|
||||
then
|
||||
if [ $failures -ne 1 ]
|
||||
then
|
||||
failures=1
|
||||
fail_test $(echo "${line}" | awk '{print $2}')
|
||||
fi
|
||||
led_on 1
|
||||
elif echo "${line}" | grep -iq '^finish'
|
||||
then
|
||||
led_on 5
|
||||
result=$(echo ${line} | awk '{print $3}')
|
||||
if [ ${result} -ge 200 -a ${result} -lt 300 ]
|
||||
then
|
||||
led_off 2
|
||||
led_off 3
|
||||
led_off 4
|
||||
else
|
||||
led_on 1
|
||||
fi
|
||||
elif echo "${line}" | grep -iq '^exit'
|
||||
then
|
||||
all_off
|
||||
exit 0
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,281 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
static volatile uint32_t piModel = 1;
|
||||
|
||||
static volatile uint32_t piPeriphBase = 0x20000000;
|
||||
static volatile uint32_t piBusAddr = 0x40000000;
|
||||
|
||||
#define SYST_BASE (piPeriphBase + 0x003000)
|
||||
#define DMA_BASE (piPeriphBase + 0x007000)
|
||||
#define CLK_BASE (piPeriphBase + 0x101000)
|
||||
#define GPIO_BASE (piPeriphBase + 0x200000)
|
||||
#define UART0_BASE (piPeriphBase + 0x201000)
|
||||
#define PCM_BASE (piPeriphBase + 0x203000)
|
||||
#define SPI0_BASE (piPeriphBase + 0x204000)
|
||||
#define I2C0_BASE (piPeriphBase + 0x205000)
|
||||
#define PWM_BASE (piPeriphBase + 0x20C000)
|
||||
#define BSCS_BASE (piPeriphBase + 0x214000)
|
||||
#define UART1_BASE (piPeriphBase + 0x215000)
|
||||
#define I2C1_BASE (piPeriphBase + 0x804000)
|
||||
#define I2C2_BASE (piPeriphBase + 0x805000)
|
||||
#define DMA15_BASE (piPeriphBase + 0xE05000)
|
||||
|
||||
#define DMA_LEN 0x1000 /* allow access to all channels */
|
||||
#define CLK_LEN 0xA8
|
||||
#define GPIO_LEN 0xB4
|
||||
#define SYST_LEN 0x1C
|
||||
#define PCM_LEN 0x24
|
||||
#define PWM_LEN 0x28
|
||||
#define I2C_LEN 0x1C
|
||||
|
||||
#define GPSET0 7
|
||||
#define GPSET1 8
|
||||
|
||||
#define GPCLR0 10
|
||||
#define GPCLR1 11
|
||||
|
||||
#define GPLEV0 13
|
||||
#define GPLEV1 14
|
||||
|
||||
#define GPPUD 37
|
||||
#define GPPUDCLK0 38
|
||||
#define GPPUDCLK1 39
|
||||
|
||||
#define SYST_CS 0
|
||||
#define SYST_CLO 1
|
||||
#define SYST_CHI 2
|
||||
|
||||
#define CLK_PASSWD (0x5A<<24)
|
||||
|
||||
#define CLK_CTL_MASH(x)((x)<<9)
|
||||
#define CLK_CTL_BUSY (1 <<7)
|
||||
#define CLK_CTL_KILL (1 <<5)
|
||||
#define CLK_CTL_ENAB (1 <<4)
|
||||
#define CLK_CTL_SRC(x) ((x)<<0)
|
||||
|
||||
#define CLK_SRCS 4
|
||||
|
||||
#define CLK_CTL_SRC_OSC 1 /* 19.2 MHz */
|
||||
#define CLK_CTL_SRC_PLLC 5 /* 1000 MHz */
|
||||
#define CLK_CTL_SRC_PLLD 6 /* 500 MHz */
|
||||
#define CLK_CTL_SRC_HDMI 7 /* 216 MHz */
|
||||
|
||||
#define CLK_DIV_DIVI(x) ((x)<<12)
|
||||
#define CLK_DIV_DIVF(x) ((x)<< 0)
|
||||
|
||||
#define CLK_GP0_CTL 28
|
||||
#define CLK_GP0_DIV 29
|
||||
#define CLK_GP1_CTL 30
|
||||
#define CLK_GP1_DIV 31
|
||||
#define CLK_GP2_CTL 32
|
||||
#define CLK_GP2_DIV 33
|
||||
|
||||
#define CLK_PCM_CTL 38
|
||||
#define CLK_PCM_DIV 39
|
||||
|
||||
#define CLK_PWM_CTL 40
|
||||
#define CLK_PWM_DIV 41
|
||||
|
||||
|
||||
static volatile uint32_t *gpioReg = MAP_FAILED;
|
||||
static volatile uint32_t *systReg = MAP_FAILED;
|
||||
static volatile uint32_t *clkReg = MAP_FAILED;
|
||||
|
||||
#define PI_BANK (gpio>>5)
|
||||
#define PI_BIT (1<<(gpio&0x1F))
|
||||
|
||||
/* gpio modes. */
|
||||
|
||||
#define PI_INPUT 0
|
||||
#define PI_OUTPUT 1
|
||||
#define PI_ALT0 4
|
||||
#define PI_ALT1 5
|
||||
#define PI_ALT2 6
|
||||
#define PI_ALT3 7
|
||||
#define PI_ALT4 3
|
||||
#define PI_ALT5 2
|
||||
|
||||
void gpioSetMode(unsigned gpio, unsigned mode) {
|
||||
int reg, shift;
|
||||
|
||||
reg = gpio/10;
|
||||
shift = (gpio%10) * 3;
|
||||
|
||||
gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
|
||||
}
|
||||
|
||||
int gpioGetMode(unsigned gpio) {
|
||||
int reg, shift;
|
||||
|
||||
reg = gpio/10;
|
||||
shift = (gpio%10) * 3;
|
||||
|
||||
return (*(gpioReg + reg) >> shift) & 7;
|
||||
}
|
||||
|
||||
/* Values for pull-ups/downs off, pull-down and pull-up. */
|
||||
|
||||
#define PI_PUD_OFF 0
|
||||
#define PI_PUD_DOWN 1
|
||||
#define PI_PUD_UP 2
|
||||
|
||||
void gpioSetPullUpDown(unsigned gpio, unsigned pud) {
|
||||
*(gpioReg + GPPUD) = pud;
|
||||
|
||||
usleep(20);
|
||||
|
||||
*(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT;
|
||||
|
||||
usleep(20);
|
||||
|
||||
*(gpioReg + GPPUD) = 0;
|
||||
|
||||
*(gpioReg + GPPUDCLK0 + PI_BANK) = 0;
|
||||
}
|
||||
|
||||
int gpioRead(unsigned gpio) {
|
||||
if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void gpioWrite(unsigned gpio, unsigned level) {
|
||||
if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
|
||||
else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
|
||||
}
|
||||
|
||||
void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level) {
|
||||
if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
|
||||
else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
|
||||
|
||||
usleep(pulseLen);
|
||||
|
||||
if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
|
||||
else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
|
||||
}
|
||||
|
||||
/* Bit (1<<x) will be set if gpio x is high. */
|
||||
|
||||
uint32_t gpioReadBank1(void) { return (*(gpioReg + GPLEV0)); }
|
||||
uint32_t gpioReadBank2(void) { return (*(gpioReg + GPLEV1)); }
|
||||
|
||||
/* To clear gpio x bit or in (1<<x). */
|
||||
|
||||
void gpioClearBank1(uint32_t bits) { *(gpioReg + GPCLR0) = bits; }
|
||||
void gpioClearBank2(uint32_t bits) { *(gpioReg + GPCLR1) = bits; }
|
||||
|
||||
/* To set gpio x bit or in (1<<x). */
|
||||
|
||||
void gpioSetBank1(uint32_t bits) { *(gpioReg + GPSET0) = bits; }
|
||||
void gpioSetBank2(uint32_t bits) { *(gpioReg + GPSET1) = bits; }
|
||||
|
||||
unsigned gpioHardwareRevision(void) {
|
||||
static unsigned rev = 0;
|
||||
|
||||
FILE * filp;
|
||||
char buf[512];
|
||||
char term;
|
||||
int chars=4; /* number of chars in revision string */
|
||||
|
||||
if (rev) return rev;
|
||||
|
||||
piModel = 0;
|
||||
|
||||
filp = fopen ("/proc/cpuinfo", "r");
|
||||
|
||||
if (filp != NULL)
|
||||
{
|
||||
while (fgets(buf, sizeof(buf), filp) != NULL)
|
||||
{
|
||||
if (piModel == 0)
|
||||
{
|
||||
if (!strncasecmp("model name", buf, 10))
|
||||
{
|
||||
if (strstr (buf, "ARMv6") != NULL)
|
||||
{
|
||||
piModel = 1;
|
||||
chars = 4;
|
||||
piPeriphBase = 0x20000000;
|
||||
piBusAddr = 0x40000000;
|
||||
}
|
||||
else if (strstr (buf, "ARMv7") != NULL)
|
||||
{
|
||||
piModel = 2;
|
||||
chars = 6;
|
||||
piPeriphBase = 0x3F000000;
|
||||
piBusAddr = 0xC0000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncasecmp("revision", buf, 8))
|
||||
{
|
||||
if (sscanf(buf+strlen(buf)-(chars+1),
|
||||
"%x%c", &rev, &term) == 2)
|
||||
{
|
||||
if (term != '\n') rev = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(filp);
|
||||
}
|
||||
return rev;
|
||||
}
|
||||
|
||||
/* Returns the number of microseconds after system boot. Wraps around
|
||||
after 1 hour 11 minutes 35 seconds.
|
||||
*/
|
||||
uint32_t gpioTick(void) {
|
||||
return systReg[SYST_CLO];
|
||||
}
|
||||
|
||||
|
||||
/* Map in registers. */
|
||||
static uint32_t *initMapMem(int fd, uint32_t addr, uint32_t len) {
|
||||
return (uint32_t *) mmap(0, len,
|
||||
PROT_READ|PROT_WRITE|PROT_EXEC,
|
||||
MAP_SHARED|MAP_LOCKED,
|
||||
fd, addr);
|
||||
}
|
||||
|
||||
int gpioInitialise(void) {
|
||||
int fd;
|
||||
|
||||
gpioHardwareRevision(); /* sets piModel, needed for peripherals address */
|
||||
|
||||
fd = open("/dev/mem", O_RDWR | O_SYNC) ;
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"This program needs root privileges. Try using sudo\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpioReg = initMapMem(fd, GPIO_BASE, GPIO_LEN);
|
||||
systReg = initMapMem(fd, SYST_BASE, SYST_LEN);
|
||||
clkReg = initMapMem(fd, CLK_BASE, CLK_LEN);
|
||||
|
||||
close(fd);
|
||||
|
||||
if ((gpioReg == MAP_FAILED) ||
|
||||
(systReg == MAP_FAILED) ||
|
||||
(clkReg == MAP_FAILED))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Bad, mmap failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef RPI_H_
|
||||
#define RPI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* gpio modes. */
|
||||
#define PI_INPUT 0
|
||||
#define PI_OUTPUT 1
|
||||
#define PI_ALT0 4
|
||||
#define PI_ALT1 5
|
||||
#define PI_ALT2 6
|
||||
#define PI_ALT3 7
|
||||
#define PI_ALT4 3
|
||||
#define PI_ALT5 2
|
||||
|
||||
void gpioSetMode(unsigned gpio, unsigned mode);
|
||||
int gpioGetMode(unsigned gpio);
|
||||
|
||||
/* Values for pull-ups/downs off, pull-down and pull-up. */
|
||||
#define PI_PUD_OFF 0
|
||||
#define PI_PUD_DOWN 1
|
||||
#define PI_PUD_UP 2
|
||||
void gpioSetPullUpDown(unsigned gpio, unsigned pud);
|
||||
|
||||
int gpioRead(unsigned gpio);
|
||||
void gpioWrite(unsigned gpio, unsigned level);
|
||||
void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level);
|
||||
|
||||
/* Bit (1<<x) will be set if gpio x is high. */
|
||||
uint32_t gpioReadBank1(void);
|
||||
uint32_t gpioReadBank2(void);
|
||||
|
||||
/* To clear gpio x bit or in (1<<x). */
|
||||
void gpioClearBank1(uint32_t bits);
|
||||
void gpioClearBank2(uint32_t bits);
|
||||
|
||||
/* To set gpio x bit or in (1<<x). */
|
||||
void gpioSetBank1(uint32_t bits);
|
||||
void gpioSetBank2(uint32_t bits);
|
||||
|
||||
unsigned gpioHardwareRevision(void);
|
||||
|
||||
/* Returns the number of microseconds after system boot. Wraps around
|
||||
after 1 hour 11 minutes 35 seconds.
|
||||
*/
|
||||
uint32_t gpioTick(void);
|
||||
|
||||
int gpioInitialise(void);
|
||||
|
||||
#endif /* RPI_H_ */
|
Binary file not shown.
|
@ -0,0 +1,63 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *log = NULL;
|
||||
struct termios tty;
|
||||
int i;
|
||||
FILE *fd;
|
||||
|
||||
if (argc > 1) {
|
||||
log = fopen(argv[1], "wb");
|
||||
if (!log) {
|
||||
perror("Couldn't open log");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 65536; i++) {
|
||||
fd = fopen("/dev/ttyACM0", "rb");
|
||||
if (fd) {
|
||||
break;
|
||||
}
|
||||
usleep(1);
|
||||
}
|
||||
if (!fd) {
|
||||
fprintf(stderr, "Unable to open after %d tries\n", i);
|
||||
perror("Couldn't open!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
tcgetattr(fileno(fd), &tty);
|
||||
cfmakeraw(&tty);
|
||||
tty.c_cflag &= ~CSTOPB;
|
||||
tty.c_cflag &= ~CRTSCTS; /* no HW flow control */
|
||||
tty.c_cflag |= CLOCAL | CREAD;
|
||||
tcsetattr(fileno(fd), TCSANOW, &tty);
|
||||
|
||||
while (1) {
|
||||
char in_bfr[1024];
|
||||
|
||||
memset(in_bfr, 0, sizeof(in_bfr));
|
||||
|
||||
if (!fgets(in_bfr, sizeof(in_bfr), fd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s", in_bfr);
|
||||
if (log)
|
||||
fprintf(log, "%s", in_bfr);
|
||||
if (!strncmp(in_bfr, "FOMU:", strlen("FOMU:"))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/sh
|
||||
|
||||
infofile=/tmp/spi-info.txt
|
||||
rm -f $infofile
|
||||
fomu-flash -t 4 -i | tee $infofile
|
||||
|
||||
# Manufacturer ID: Macronix (c2)
|
||||
# Memory model: MX25R1635F (28)
|
||||
# Memory size: 16 Mbit (15)
|
||||
# Device ID: 15
|
||||
|
||||
error_count=0
|
||||
if ! grep -q 'Manufacturer ID: Macronix (c2)' $infofile
|
||||
then
|
||||
echo -n "Unrecognized SPI manufacturer: "
|
||||
grep 'Manufacturer ID: ' $infofile
|
||||
error_count=$(($error_count+1))
|
||||
fi
|
||||
|
||||
if ! grep -q 'Memory model: MX25R1635F (28)' $infofile
|
||||
then
|
||||
echo -n "Unrecognized memory model: "
|
||||
grep 'Memory model: ' $infofile
|
||||
error_count=$(($error_count+1))
|
||||
fi
|
||||
|
||||
if ! grep -q 'Memory size: 16 Mbit (15)' $infofile
|
||||
then
|
||||
echo -n "Unrecognized memory size: "
|
||||
grep 'Memory size: ' $infofile
|
||||
error_count=$(($error_count+1))
|
||||
fi
|
||||
|
||||
if ! grep -q 'Device ID: 15' $infofile
|
||||
then
|
||||
echo -n "Unrecognized device id: "
|
||||
grep 'Device ID: ' $infofile
|
||||
error_count=$(($error_count+1))
|
||||
fi
|
||||
|
||||
exit $error_count
|
|
@ -0,0 +1,7 @@
|
|||
[Test]
|
||||
ExecStart=/bin/true
|
||||
Name=Run all tests
|
||||
Description=Make sure all tests complete successfully
|
||||
#Requires=test-rgbb, test-rgbg, test-rgbr, test-spi, test-touch
|
||||
Requires=test-spi, test-touch
|
||||
Timeout=4
|
|
@ -0,0 +1,5 @@
|
|||
[Scenario]
|
||||
Name=Program and test Everything
|
||||
Description=Load the test bitstream, run tests, then load the final bitstream
|
||||
Tests=all-tests,load-final-bitstream,verify-final-bitstream
|
||||
Timeout=5
|
|
@ -0,0 +1,6 @@
|
|||
[Jig]
|
||||
Name=Raspberry Pi Fomu PVT Jig
|
||||
Description=Fomu factory jig running on a Raspberry Pi, designed to burn and test Fomu PVT
|
||||
TestFile=/etc
|
||||
DefaultScenario=complete
|
||||
DefaultWorkingDirectory=../bin
|
|
@ -0,0 +1,5 @@
|
|||
[Logger]
|
||||
Name=File Logger
|
||||
Description=Write log messages out to a file
|
||||
ExecStart=/bin/sh -c "cat >> /home/fomu/fomu-test-log.json"
|
||||
Format=json
|
|
@ -0,0 +1,5 @@
|
|||
[Interface]
|
||||
Name=Front panel LEDs
|
||||
Description=Shows test status on the LEDs
|
||||
Format=text
|
||||
ExecStart=./led-interface.sh
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=fomu-flash -w pvt-top-multiboot.bin
|
||||
Name=Load Final Bitstream
|
||||
Description=Use fomu-flash to load the final bitstream
|
||||
Requires=all-tests
|
||||
Timeout=3
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=fomu-flash -f pvt-tester-bitstream.bin
|
||||
Name=Load Tester Bitstream
|
||||
Description=Use fomu-flash to load the tester bitstream
|
||||
Requires=validate-spi
|
||||
Timeout=1
|
|
@ -0,0 +1,4 @@
|
|||
[Trigger]
|
||||
Name=Start button
|
||||
Description=A start button on the Raspberry Pi jig, pin 26. Compile with 'gcc gpiopoll.c -o gpiopoll; sudo chown root gpiopoll; sudo chmod u+s gpiopoll'
|
||||
ExecStart=./gpiopoll 26
|
|
@ -0,0 +1,7 @@
|
|||
[Test]
|
||||
ExecStart=./uart-monitor /tmp/monitor-log.txt
|
||||
Name=Run all tests
|
||||
Description=Run all tests and log the output to a file
|
||||
Requires=load-tester-bitstream
|
||||
Required=load-tester-bitstream
|
||||
Timeout=2
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=grep -c '^RGBB: .*Pass\s*$' /tmp/monitor-log.txt
|
||||
Name=Verify Blue LED
|
||||
Description=Look for "RGBB: Pass" in the monitor log
|
||||
Requires=load-tester-bitstream,run-all-tests
|
||||
Timeout=1
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=grep -c '^RGBG: .*Pass\s*$' /tmp/monitor-log.txt
|
||||
Name=Verify Green LED
|
||||
Description=Look for "RGBG: Pass" in the monitor log
|
||||
Requires=load-tester-bitstream,run-all-tests
|
||||
Timeout=1
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=grep -c '^RGBR: .*Pass\s*$' /tmp/monitor-log.txt
|
||||
Name=Verify Red LED
|
||||
Description=Look for "RGBR: Pass" in the monitor log
|
||||
Requires=load-tester-bitstream,run-all-tests
|
||||
Timeout=1
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=grep -c '^SPI: .*Pass\s*$' /tmp/monitor-log.txt
|
||||
Name=Verify SPI
|
||||
Description=Look for "SPI: Pass" in the monitor log
|
||||
Requires=load-tester-bitstream,run-all-tests
|
||||
Timeout=1
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=grep -c '^TOUCH: .*Pass\s*$' /tmp/monitor-log.txt
|
||||
Name=Verify Touch Pads
|
||||
Description=Look for "TOUCH: Pass" in the monitor log
|
||||
Requires=load-tester-bitstream,run-all-tests
|
||||
Timeout=3
|
|
@ -0,0 +1,5 @@
|
|||
[Test]
|
||||
ExecStart=./validate-spi.sh
|
||||
Name=Validate SPI
|
||||
Description=Validate that the SPI part number is what we expect
|
||||
Timeout=1
|
|
@ -0,0 +1,6 @@
|
|||
[Test]
|
||||
ExecStart=fomu-flash -v pvt-top-multiboot.bin
|
||||
Name=Verify Final Bitstream
|
||||
Description=Use fomu-flash to verify the final bitstream
|
||||
Requires=load-final-bitstream
|
||||
Timeout=3
|
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
# copy this file to /etc/systemd/system
|
||||
# then run:
|
||||
# sudo systemctl start exclave
|
||||
# sudo systemctl stop exclave
|
||||
# sudo systemctl enable exclave
|
||||
|
||||
[Unit]
|
||||
Description=Exclave tester automation framework service
|
||||
After=network.target
|
||||
After=systemd-user-sessions.service
|
||||
|
||||
[Service]
|
||||
User=pi
|
||||
ExecStart=/home/pi/code/netv2-tests/run_exclave.sh
|
||||
#Restart=always
|
||||
#TimeoutSec=60 # I have no idea how to tell systemd the script has started...
|
||||
#RestartSec=10
|
||||
#StartTimeLimitInterval=60
|
||||
#StartLimitBurst=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -49,7 +49,7 @@ CFLAGS := $(ADD_CFLAGS) \
|
|||
-ffunction-sections -fdata-sections -fno-common \
|
||||
-fomit-frame-pointer -Os \
|
||||
-march=rv32i \
|
||||
-DGIT_VERSION=u\"$(GIT_VERSION)\" -std=gnu11
|
||||
-DGIT_VERSION=\"$(GIT_VERSION)\" -std=gnu11
|
||||
CXXFLAGS := $(CFLAGS) -std=c++11 -fno-rtti -fno-exceptions
|
||||
LFLAGS := $(CFLAGS) $(ADD_LFLAGS) -L$(LD_DIR) \
|
||||
-nostartfiles \
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
#ifndef __HW_COMMON_H
|
||||
#define __HW_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* To overwrite CSR accessors, define extern, non-inlined versions
|
||||
* of csr_read[bwl]() and csr_write[bwl](), and define
|
||||
* CSR_ACCESSORS_DEFINED.
|
||||
*/
|
||||
|
||||
#ifndef CSR_ACCESSORS_DEFINED
|
||||
#define CSR_ACCESSORS_DEFINED
|
||||
|
||||
#ifdef __ASSEMBLER__
|
||||
#define MMPTR(x) x
|
||||
#else /* ! __ASSEMBLER__ */
|
||||
#define MMPTR(x) (*((volatile unsigned int *)(x)))
|
||||
|
||||
static inline void csr_writeb(uint8_t value, uint32_t addr)
|
||||
{
|
||||
*((volatile uint8_t *)addr) = value;
|
||||
}
|
||||
|
||||
static inline uint8_t csr_readb(uint32_t addr)
|
||||
{
|
||||
return *(volatile uint8_t *)addr;
|
||||
}
|
||||
|
||||
static inline void csr_writew(uint16_t value, uint32_t addr)
|
||||
{
|
||||
*((volatile uint16_t *)addr) = value;
|
||||
}
|
||||
|
||||
static inline uint16_t csr_readw(uint32_t addr)
|
||||
{
|
||||
return *(volatile uint16_t *)addr;
|
||||
}
|
||||
|
||||
static inline void csr_writel(uint32_t value, uint32_t addr)
|
||||
{
|
||||
*((volatile uint32_t *)addr) = value;
|
||||
}
|
||||
|
||||
static inline uint32_t csr_readl(uint32_t addr)
|
||||
{
|
||||
return *(volatile uint32_t *)addr;
|
||||
}
|
||||
#endif /* ! __ASSEMBLER__ */
|
||||
|
||||
#endif /* ! CSR_ACCESSORS_DEFINED */
|
||||
|
||||
#endif /* __HW_COMMON_H */
|
|
@ -0,0 +1,117 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// \author (c) Marco Paland (info@paland.com)
|
||||
// 2014-2019, PALANDesign Hannover, Germany
|
||||
//
|
||||
// \license The MIT License (MIT)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
|
||||
// embedded systems with a very limited resources.
|
||||
// Use this instead of bloated standard/newlib printf.
|
||||
// These routines are thread safe and reentrant.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _PRINTF_H_
|
||||
#define _PRINTF_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Output a character to a custom device like UART, used by the printf() function
|
||||
* This function is declared here only. You have to write your custom implementation somewhere
|
||||
* \param character Character to output
|
||||
*/
|
||||
void _putchar(char character);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny printf implementation
|
||||
* You have to implement _putchar if you use printf()
|
||||
* To avoid conflicts with the regular printf() API it is overridden by macro defines
|
||||
* and internal underscore-appended functions like printf_() are used
|
||||
* \param format A string that specifies the format of the output
|
||||
* \return The number of characters that are written into the array, not counting the terminating null character
|
||||
*/
|
||||
#define printf printf_
|
||||
int printf_(const char* format, ...);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny sprintf implementation
|
||||
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
|
||||
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
|
||||
* \param format A string that specifies the format of the output
|
||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
|
||||
*/
|
||||
#define sprintf sprintf_
|
||||
int sprintf_(char* buffer, const char* format, ...);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny snprintf/vsnprintf implementation
|
||||
* \param buffer A pointer to the buffer where to store the formatted string
|
||||
* \param count The maximum number of characters to store in the buffer, including a terminating null character
|
||||
* \param format A string that specifies the format of the output
|
||||
* \param va A value identifying a variable arguments list
|
||||
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
|
||||
* null character. A value equal or larger than count indicates truncation. Only when the returned value
|
||||
* is non-negative and less than count, the string has been completely written.
|
||||
*/
|
||||
#define snprintf snprintf_
|
||||
#define vsnprintf vsnprintf_
|
||||
int snprintf_(char* buffer, size_t count, const char* format, ...);
|
||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny vprintf implementation
|
||||
* \param format A string that specifies the format of the output
|
||||
* \param va A value identifying a variable arguments list
|
||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
|
||||
*/
|
||||
#define vprintf vprintf_
|
||||
int vprintf_(const char* format, va_list va);
|
||||
|
||||
|
||||
/**
|
||||
* printf with output function
|
||||
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
|
||||
* \param out An output function which takes one character and an argument pointer
|
||||
* \param arg An argument pointer for user data passed to output function
|
||||
* \param format A string that specifies the format of the output
|
||||
* \return The number of characters that are sent to the output function, not counting the terminating null character
|
||||
*/
|
||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _PRINTF_H_
|
|
@ -6,5 +6,6 @@ void rgb_mode_idle(void);
|
|||
void rgb_mode_done(void);
|
||||
void rgb_mode_writing(void);
|
||||
void rgb_mode_error(void);
|
||||
void rgb_mode_off(void);
|
||||
|
||||
#endif /* _RGB_H_ */
|
|
@ -50,45 +50,43 @@ struct spi_id {
|
|||
|
||||
struct ff_spi;
|
||||
|
||||
void spiPause(struct ff_spi *spi);
|
||||
void spiBegin(struct ff_spi *spi);
|
||||
void spiEnd(struct ff_spi *spi);
|
||||
void spiPause(void);
|
||||
void spiBegin(void);
|
||||
void spiEnd(void);
|
||||
|
||||
//void spiSingleTx(struct ff_spi *spi, uint8_t out);
|
||||
//uint8_t spiSingleRx(struct ff_spi *spi);
|
||||
//void spiDualTx(struct ff_spi *spi, uint8_t out);
|
||||
//void spiQuadTx(struct ff_spi *spi, uint8_t out);
|
||||
void spiCommand(struct ff_spi *spi, uint8_t cmd);
|
||||
//uint8_t spiDualRx(struct ff_spi *spi);
|
||||
//uint8_t spiQuadRx(struct ff_spi *spi);
|
||||
int spiTx(struct ff_spi *spi, uint8_t word);
|
||||
uint8_t spiRx(struct ff_spi *spi);
|
||||
uint8_t spiReadStatus(struct ff_spi *spi, uint8_t sr);
|
||||
void spiWriteStatus(struct ff_spi *spi, uint8_t sr, uint8_t val);
|
||||
void spiReadSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]);
|
||||
void spiWriteSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]);
|
||||
int spiSetType(struct ff_spi *spi, enum spi_type type);
|
||||
int spiRead(struct ff_spi *spi, uint32_t addr, uint8_t *data, unsigned int count);
|
||||
int spiIsBusy(struct ff_spi *spi);
|
||||
int spiBeginErase32(struct ff_spi *spi, uint32_t erase_addr);
|
||||
int spiBeginErase64(struct ff_spi *spi, uint32_t erase_addr);
|
||||
int spiBeginWrite(struct ff_spi *spi, uint32_t addr, const void *data, unsigned int count);
|
||||
//void spiSingleTx(uint8_t out);
|
||||
//uint8_t spiSingleRx(void);
|
||||
//void spiDualTx(uint8_t out);
|
||||
//void spiQuadTx(uint8_t out);
|
||||
void spiCommand(uint8_t cmd);
|
||||
//uint8_t spiDualRx(void);
|
||||
//uint8_t spiQuadRx(void);
|
||||
int spiTx(uint8_t word);
|
||||
uint8_t spiRx(void);
|
||||
uint8_t spiReadStatus(uint8_t sr);
|
||||
void spiWriteStatus(uint8_t sr, uint8_t val);
|
||||
void spiReadSecurity(uint8_t sr, uint8_t security[256]);
|
||||
void spiWriteSecurity(uint8_t sr, uint8_t security[256]);
|
||||
int spiSetType(enum spi_type type);
|
||||
int spiRead(uint32_t addr, uint8_t *data, unsigned int count);
|
||||
int spiIsBusy(void);
|
||||
int spiBeginErase32(uint32_t erase_addr);
|
||||
int spiBeginErase64(uint32_t erase_addr);
|
||||
int spiBeginWrite(uint32_t addr, const void *data, unsigned int count);
|
||||
void spiEnableQuad(void);
|
||||
|
||||
struct spi_id spiId(struct ff_spi *spi);
|
||||
void spiOverrideSize(struct ff_spi *spi, uint32_t new_size);
|
||||
struct spi_id spiId(void);
|
||||
void spiOverrideSize(uint32_t new_size);
|
||||
|
||||
//int spi_wait_for_not_busy(struct ff_spi *spi);
|
||||
int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned int count);
|
||||
uint8_t spiReset(struct ff_spi *spi);
|
||||
int spiInit(struct ff_spi *spi);
|
||||
int spiWrite(uint32_t addr, const uint8_t *data, unsigned int count);
|
||||
uint8_t spiReset(void);
|
||||
int spi_init(void);
|
||||
|
||||
void spiHold(struct ff_spi *spi);
|
||||
void spiUnhold(struct ff_spi *spi);
|
||||
void spiSwapTxRx(struct ff_spi *spi);
|
||||
void spiHold(void);
|
||||
void spiUnhold(void);
|
||||
void spiSwapTxRx(void);
|
||||
|
||||
struct ff_spi *spiAlloc(void);
|
||||
void spiSetPin(struct ff_spi *spi, enum spi_pin pin, int val);
|
||||
void spiSetPin(enum spi_pin pin, int val);
|
||||
void spiFree(void);
|
||||
|
||||
#endif /* BB_SPI_H_ */
|
||||
|
|
|
@ -160,6 +160,11 @@ struct usb_cdc_notification {
|
|||
|
||||
int cdc_connected();
|
||||
void cdc_set_connected(int is_connected);
|
||||
void put_hex(uint32_t val);
|
||||
void put_hex_byte(uint8_t val);
|
||||
void put_string(const char *str);
|
||||
void put_char(char character);
|
||||
void flush_serial(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ struct usb_string_descriptor_struct {
|
|||
#define PRODUCT_NAME u"Fomu Factory Test " GIT_VERSION
|
||||
#define PRODUCT_NAME_LEN sizeof(PRODUCT_NAME)
|
||||
#define EP0_SIZE 64
|
||||
#define NUM_INTERFACE 1
|
||||
#define NUM_INTERFACE 2
|
||||
#define CONFIG_DESC_SIZE 67
|
||||
#define USB_DT_INTERFACE_SIZE 9
|
||||
|
||||
|
|
|
@ -7,6 +7,25 @@ extern "C" {
|
|||
|
||||
struct usb_setup_request;
|
||||
|
||||
enum epfifo_response {
|
||||
EPF_ACK = 0,
|
||||
EPF_NAK = 1,
|
||||
EPF_NONE = 2,
|
||||
EPF_STALL = 3,
|
||||
};
|
||||
|
||||
// Note that our PIDs are only bits 2 and 3 of the token,
|
||||
// since all other bits are effectively redundant at this point.
|
||||
enum USB_PID {
|
||||
USB_PID_OUT = 0,
|
||||
USB_PID_SOF = 1,
|
||||
USB_PID_IN = 2,
|
||||
USB_PID_SETUP = 3,
|
||||
};
|
||||
|
||||
#define USB_EV_ERROR 1
|
||||
#define USB_EV_PACKET 2
|
||||
|
||||
void usb_isr(void);
|
||||
void usb_init(void);
|
||||
void usb_connect(void);
|
||||
|
@ -14,7 +33,7 @@ void usb_idle(void);
|
|||
void usb_disconnect(void);
|
||||
|
||||
int usb_irq_happened(void);
|
||||
void usb_setup(const struct usb_setup_request *setup);
|
||||
int usb_setup(const struct usb_setup_request *setup);
|
||||
void usb_send(const void *data, int total_count);
|
||||
void usb_ack_in(void);
|
||||
void usb_ack_out(void);
|
||||
|
|
|
@ -6,13 +6,6 @@
|
|||
|
||||
_start:
|
||||
j crt_init
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
.section .text
|
||||
.global trap_entry
|
||||
|
@ -55,7 +48,6 @@ trap_entry:
|
|||
mret
|
||||
.text
|
||||
|
||||
|
||||
crt_init:
|
||||
la sp, _fstack + 4
|
||||
la a0, trap_entry
|
||||
|
@ -87,5 +79,6 @@ bss_done:
|
|||
csrw mie,a0
|
||||
|
||||
call main
|
||||
infinit_loop:
|
||||
j infinit_loop
|
||||
|
||||
infinite_loop:
|
||||
j infinite_loop
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
.text
|
||||
.align 2
|
||||
|
||||
#ifndef __riscv64
|
||||
/* Our RV64 64-bit routines are equivalent to our RV32 32-bit routines. */
|
||||
# define __udivdi3 __udivsi3
|
||||
# define __umoddi3 __umodsi3
|
||||
# define __divdi3 __divsi3
|
||||
# define __moddi3 __modsi3
|
||||
#else
|
||||
.globl __udivsi3
|
||||
__udivsi3:
|
||||
/* Compute __udivdi3(a0 << 32, a1 << 32); cast result to uint32_t. */
|
||||
sll a0, a0, 32
|
||||
sll a1, a1, 32
|
||||
move t0, ra
|
||||
jal __udivdi3
|
||||
sext.w a0, a0
|
||||
jr t0
|
||||
|
||||
.globl __umodsi3
|
||||
__umodsi3:
|
||||
/* Compute __udivdi3((uint32_t)a0, (uint32_t)a1); cast a1 to uint32_t. */
|
||||
sll a0, a0, 32
|
||||
sll a1, a1, 32
|
||||
srl a0, a0, 32
|
||||
srl a1, a1, 32
|
||||
move t0, ra
|
||||
jal __udivdi3
|
||||
sext.w a0, a1
|
||||
jr t0
|
||||
|
||||
.globl __modsi3
|
||||
__modsi3 = __moddi3
|
||||
|
||||
.globl __divsi3
|
||||
__divsi3:
|
||||
/* Check for special case of INT_MIN/-1. Otherwise, fall into __divdi3. */
|
||||
li t0, -1
|
||||
beq a1, t0, .L20
|
||||
#endif
|
||||
|
||||
.globl __divdi3
|
||||
__divdi3:
|
||||
bltz a0, .L10
|
||||
bltz a1, .L11
|
||||
/* Since the quotient is positive, fall into __udivdi3. */
|
||||
|
||||
.globl __udivdi3
|
||||
__udivdi3:
|
||||
mv a2, a1
|
||||
mv a1, a0
|
||||
li a0, -1
|
||||
beqz a2, .L5
|
||||
li a3, 1
|
||||
bgeu a2, a1, .L2
|
||||
.L1:
|
||||
blez a2, .L2
|
||||
slli a2, a2, 1
|
||||
slli a3, a3, 1
|
||||
bgtu a1, a2, .L1
|
||||
.L2:
|
||||
li a0, 0
|
||||
.L3:
|
||||
bltu a1, a2, .L4
|
||||
sub a1, a1, a2
|
||||
or a0, a0, a3
|
||||
.L4:
|
||||
srli a3, a3, 1
|
||||
srli a2, a2, 1
|
||||
bnez a3, .L3
|
||||
.L5:
|
||||
ret
|
||||
|
||||
.globl __umoddi3
|
||||
__umoddi3:
|
||||
/* Call __udivdi3(a0, a1), then return the remainder, which is in a1. */
|
||||
move t0, ra
|
||||
jal __udivdi3
|
||||
move a0, a1
|
||||
jr t0
|
||||
|
||||
/* Handle negative arguments to __divdi3. */
|
||||
.L10:
|
||||
neg a0, a0
|
||||
bgez a1, .L12 /* Compute __udivdi3(-a0, a1), then negate the result. */
|
||||
neg a1, a1
|
||||
j __divdi3 /* Compute __udivdi3(-a0, -a1). */
|
||||
.L11: /* Compute __udivdi3(a0, -a1), then negate the result. */
|
||||
neg a1, a1
|
||||
.L12:
|
||||
move t0, ra
|
||||
jal __divdi3
|
||||
neg a0, a0
|
||||
jr t0
|
||||
|
||||
.globl __moddi3
|
||||
__moddi3:
|
||||
move t0, ra
|
||||
bltz a1, .L31
|
||||
bltz a0, .L32
|
||||
.L30:
|
||||
jal __udivdi3 /* The dividend is not negative. */
|
||||
move a0, a1
|
||||
jr t0
|
||||
.L31:
|
||||
neg a1, a1
|
||||
bgez a0, .L30
|
||||
.L32:
|
||||
neg a0, a0
|
||||
jal __udivdi3 /* The dividend is hella negative. */
|
||||
neg a0, a1
|
||||
jr t0
|
||||
|
||||
#ifdef __riscv64
|
||||
/* continuation of __divsi3 */
|
||||
.L20:
|
||||
sll t0, t0, 31
|
||||
bne a0, t0, __divdi3
|
||||
ret
|
||||
#endif
|
|
@ -1,10 +1,11 @@
|
|||
#include <stdio.h>
|
||||
#include <fomu/csr.h>
|
||||
#include <irq.h>
|
||||
#include <usb.h>
|
||||
#include <time.h>
|
||||
#include <rgb.h>
|
||||
#include <spi.h>
|
||||
#include <fomu/csr.h>
|
||||
#include <tester.h>
|
||||
#include <usb-cdc.h>
|
||||
|
||||
struct ff_spi *spi;
|
||||
|
||||
|
@ -21,13 +22,11 @@ void isr(void)
|
|||
static void init(void)
|
||||
{
|
||||
rgb_init();
|
||||
spi = spiAlloc();
|
||||
spiInit(spi);
|
||||
spi_init();
|
||||
irq_setmask(0);
|
||||
irq_setie(1);
|
||||
usb_init();
|
||||
time_init();
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
.text
|
||||
.align 2
|
||||
|
||||
#ifdef __riscv64
|
||||
#define _RISCV_SZPTR 64
|
||||
#define _RISCV_SZINT 64
|
||||
#else
|
||||
/* Our RV64 64-bit routine is equivalent to our RV32 32-bit routine. */
|
||||
# define __muldi3 __mulsi3
|
||||
#define _RISCV_SZPTR 32
|
||||
#define _RISCV_SZINT 32
|
||||
#endif
|
||||
|
||||
.globl __muldi3
|
||||
__muldi3:
|
||||
mv a2, a0
|
||||
li a0, 0
|
||||
.L1:
|
||||
slli a3, a1, _RISCV_SZPTR-1
|
||||
bgez a3, .L2
|
||||
add a0, a0, a2
|
||||
.L2:
|
||||
srli a1, a1, 1
|
||||
slli a2, a2, 1
|
||||
bnez a1, .L1
|
||||
ret
|
|
@ -0,0 +1,914 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// \author (c) Marco Paland (info@paland.com)
|
||||
// 2014-2019, PALANDesign Hannover, Germany
|
||||
//
|
||||
// \license The MIT License (MIT)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
|
||||
// embedded systems with a very limited resources. These routines are thread
|
||||
// safe and reentrant!
|
||||
// Use this instead of the bloated standard/newlib printf cause these use
|
||||
// malloc for printf (and may not be thread safe).
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "printf.h"
|
||||
|
||||
|
||||
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
|
||||
// printf_config.h header file
|
||||
// default: undefined
|
||||
// #ifdef PRINTF_INCLUDE_CONFIG_H
|
||||
// #include "printf_config.h"
|
||||
// #endif
|
||||
|
||||
|
||||
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
|
||||
// numeric number including padded zeros (dynamically created on stack)
|
||||
// default: 32 byte
|
||||
#ifndef PRINTF_NTOA_BUFFER_SIZE
|
||||
#define PRINTF_NTOA_BUFFER_SIZE 32U
|
||||
#endif
|
||||
|
||||
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
|
||||
// float number including padded zeros (dynamically created on stack)
|
||||
// default: 32 byte
|
||||
#ifndef PRINTF_FTOA_BUFFER_SIZE
|
||||
#define PRINTF_FTOA_BUFFER_SIZE 32U
|
||||
#endif
|
||||
|
||||
// support for the floating point type (%f)
|
||||
// default: activated
|
||||
// #ifndef PRINTF_DISABLE_SUPPORT_FLOAT
|
||||
// #define PRINTF_SUPPORT_FLOAT
|
||||
// #endif
|
||||
|
||||
// support for exponential floating point notation (%e/%g)
|
||||
// default: activated
|
||||
// #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
|
||||
// #define PRINTF_SUPPORT_EXPONENTIAL
|
||||
// #endif
|
||||
|
||||
// define the default floating point precision
|
||||
// default: 6 digits
|
||||
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
|
||||
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
|
||||
#endif
|
||||
|
||||
// define the largest float suitable to print with %f
|
||||
// default: 1e9
|
||||
#ifndef PRINTF_MAX_FLOAT
|
||||
#define PRINTF_MAX_FLOAT 1e9
|
||||
#endif
|
||||
|
||||
// support for the long long types (%llu or %p)
|
||||
// default: activated
|
||||
// #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
|
||||
// #define PRINTF_SUPPORT_LONG_LONG
|
||||
// #endif
|
||||
|
||||
// support for the ptrdiff_t type (%t)
|
||||
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
|
||||
// default: activated
|
||||
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
|
||||
// #define PRINTF_SUPPORT_PTRDIFF_T
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// internal flag definitions
|
||||
#define FLAGS_ZEROPAD (1U << 0U)
|
||||
#define FLAGS_LEFT (1U << 1U)
|
||||
#define FLAGS_PLUS (1U << 2U)
|
||||
#define FLAGS_SPACE (1U << 3U)
|
||||
#define FLAGS_HASH (1U << 4U)
|
||||
#define FLAGS_UPPERCASE (1U << 5U)
|
||||
#define FLAGS_CHAR (1U << 6U)
|
||||
#define FLAGS_SHORT (1U << 7U)
|
||||
#define FLAGS_LONG (1U << 8U)
|
||||
#define FLAGS_LONG_LONG (1U << 9U)
|
||||
#define FLAGS_PRECISION (1U << 10U)
|
||||
#define FLAGS_ADAPT_EXP (1U << 11U)
|
||||
|
||||
|
||||
// import float.h for DBL_MAX
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
|
||||
// output function type
|
||||
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
|
||||
|
||||
|
||||
// wrapper (used as buffer) for output function type
|
||||
typedef struct {
|
||||
void (*fct)(char character, void* arg);
|
||||
void* arg;
|
||||
} out_fct_wrap_type;
|
||||
|
||||
|
||||
// internal buffer output
|
||||
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
if (idx < maxlen) {
|
||||
((char*)buffer)[idx] = character;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal null output
|
||||
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
(void)character; (void)buffer; (void)idx; (void)maxlen;
|
||||
}
|
||||
|
||||
|
||||
// internal _putchar wrapper
|
||||
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
(void)buffer; (void)idx; (void)maxlen;
|
||||
if (character) {
|
||||
_putchar(character);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal output function wrapper
|
||||
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
(void)idx; (void)maxlen;
|
||||
if (character) {
|
||||
// buffer is the output fct pointer
|
||||
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal secure strlen
|
||||
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
|
||||
static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
|
||||
{
|
||||
const char* s;
|
||||
for (s = str; *s && maxsize--; ++s);
|
||||
return (unsigned int)(s - str);
|
||||
}
|
||||
|
||||
|
||||
// internal test if char is a digit (0-9)
|
||||
// \return true if char is a digit
|
||||
static inline bool _is_digit(char ch)
|
||||
{
|
||||
return (ch >= '0') && (ch <= '9');
|
||||
}
|
||||
|
||||
|
||||
// internal ASCII string to unsigned int conversion
|
||||
static unsigned int _atoi(const char** str)
|
||||
{
|
||||
unsigned int i = 0U;
|
||||
while (_is_digit(**str)) {
|
||||
i = i * 10U + (unsigned int)(*((*str)++) - '0');
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// output the specified string in reverse, taking care of any zero-padding
|
||||
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
|
||||
{
|
||||
const size_t start_idx = idx;
|
||||
|
||||
// pad spaces up to given width
|
||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
||||
for (size_t i = len; i < width; i++) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
// reverse string
|
||||
while (len) {
|
||||
out(buf[--len], buffer, idx++, maxlen);
|
||||
}
|
||||
|
||||
// append pad spaces up to given width
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (idx - start_idx < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
// internal itoa format
|
||||
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
// pad leading zeros
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
|
||||
width--;
|
||||
}
|
||||
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (flags & FLAGS_HASH) {
|
||||
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
|
||||
len--;
|
||||
if (len && (base == 16U)) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'x';
|
||||
}
|
||||
else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'X';
|
||||
}
|
||||
else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'b';
|
||||
}
|
||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
||||
if (negative) {
|
||||
buf[len++] = '-';
|
||||
}
|
||||
else if (flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
}
|
||||
else if (flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
|
||||
}
|
||||
|
||||
|
||||
// internal itoa for 'long' type
|
||||
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) {
|
||||
flags &= ~FLAGS_HASH;
|
||||
}
|
||||
|
||||
// write if precision != 0 and value is != 0
|
||||
if (!(flags & FLAGS_PRECISION) || value) {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
||||
value /= base;
|
||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
||||
}
|
||||
|
||||
|
||||
// internal itoa for 'long long' type
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) {
|
||||
flags &= ~FLAGS_HASH;
|
||||
}
|
||||
|
||||
// write if precision != 0 and value is != 0
|
||||
if (!(flags & FLAGS_PRECISION) || value) {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
||||
value /= base;
|
||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
||||
}
|
||||
#endif // PRINTF_SUPPORT_LONG_LONG
|
||||
|
||||
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
|
||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
||||
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
|
||||
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
|
||||
#endif
|
||||
|
||||
|
||||
// internal ftoa for fixed decimal floating point
|
||||
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
char buf[PRINTF_FTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
double diff = 0.0;
|
||||
|
||||
// powers of 10
|
||||
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
|
||||
// test for special values
|
||||
if (value != value)
|
||||
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
|
||||
if (value < -DBL_MAX)
|
||||
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
|
||||
if (value > DBL_MAX)
|
||||
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
|
||||
|
||||
// test for very large values
|
||||
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
|
||||
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
|
||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
||||
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
|
||||
#else
|
||||
return 0U;
|
||||
#endif
|
||||
}
|
||||
|
||||
// test for negative
|
||||
bool negative = false;
|
||||
if (value < 0) {
|
||||
negative = true;
|
||||
value = 0 - value;
|
||||
}
|
||||
|
||||
// set default precision, if not set explicitly
|
||||
if (!(flags & FLAGS_PRECISION)) {
|
||||
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
}
|
||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
|
||||
buf[len++] = '0';
|
||||
prec--;
|
||||
}
|
||||
|
||||
int whole = (int)value;
|
||||
double tmp = (value - whole) * pow10[prec];
|
||||
unsigned long frac = (unsigned long)tmp;
|
||||
diff = tmp - frac;
|
||||
|
||||
if (diff > 0.5) {
|
||||
++frac;
|
||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
||||
if (frac >= pow10[prec]) {
|
||||
frac = 0;
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else if (diff < 0.5) {
|
||||
}
|
||||
else if ((frac == 0U) || (frac & 1U)) {
|
||||
// if halfway, round up if odd OR if last digit is 0
|
||||
++frac;
|
||||
}
|
||||
|
||||
if (prec == 0U) {
|
||||
diff = value - (double)whole;
|
||||
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
|
||||
// exactly 0.5 and ODD, then round up
|
||||
// 1.5 -> 2, but 2.5 -> 2
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int count = prec;
|
||||
// now do fractional part, as an unsigned number
|
||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
--count;
|
||||
buf[len++] = (char)(48U + (frac % 10U));
|
||||
if (!(frac /= 10U)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// add extra 0s
|
||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
// add decimal
|
||||
buf[len++] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
// do whole part, number is reversed
|
||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
buf[len++] = (char)(48 + (whole % 10));
|
||||
if (!(whole /= 10)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pad leading zeros
|
||||
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
|
||||
if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
|
||||
width--;
|
||||
}
|
||||
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
if (negative) {
|
||||
buf[len++] = '-';
|
||||
}
|
||||
else if (flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
}
|
||||
else if (flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
|
||||
}
|
||||
|
||||
|
||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
||||
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
|
||||
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
// check for NaN and special values
|
||||
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
|
||||
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
|
||||
}
|
||||
|
||||
// determine the sign
|
||||
const bool negative = value < 0;
|
||||
if (negative) {
|
||||
value = -value;
|
||||
}
|
||||
|
||||
// default precision
|
||||
if (!(flags & FLAGS_PRECISION)) {
|
||||
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
}
|
||||
|
||||
// determine the decimal exponent
|
||||
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
|
||||
union {
|
||||
uint64_t U;
|
||||
double F;
|
||||
} conv;
|
||||
|
||||
conv.F = value;
|
||||
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
|
||||
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
|
||||
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
|
||||
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
|
||||
// now we want to compute 10^expval but we want to be sure it won't overflow
|
||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
||||
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
||||
const double z2 = z * z;
|
||||
conv.U = (uint64_t)(exp2 + 1023) << 52U;
|
||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
||||
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
||||
// correct for rounding errors
|
||||
if (value < conv.F) {
|
||||
expval--;
|
||||
conv.F /= 10;
|
||||
}
|
||||
|
||||
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
|
||||
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
|
||||
|
||||
// in "%g" mode, "prec" is the number of *significant figures* not decimals
|
||||
if (flags & FLAGS_ADAPT_EXP) {
|
||||
// do we want to fall-back to "%f" mode?
|
||||
if ((value >= 1e-4) && (value < 1e6)) {
|
||||
if ((int)prec > expval) {
|
||||
prec = (unsigned)((int)prec - expval - 1);
|
||||
}
|
||||
else {
|
||||
prec = 0;
|
||||
}
|
||||
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
|
||||
// no characters in exponent
|
||||
minwidth = 0U;
|
||||
expval = 0;
|
||||
}
|
||||
else {
|
||||
// we use one sigfig for the whole part
|
||||
if ((prec > 0) && (flags & FLAGS_PRECISION)) {
|
||||
--prec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// will everything fit?
|
||||
unsigned int fwidth = width;
|
||||
if (width > minwidth) {
|
||||
// we didn't fall-back so subtract the characters required for the exponent
|
||||
fwidth -= minwidth;
|
||||
} else {
|
||||
// not enough characters, so go back to default sizing
|
||||
fwidth = 0U;
|
||||
}
|
||||
if ((flags & FLAGS_LEFT) && minwidth) {
|
||||
// if we're padding on the right, DON'T pad the floating part
|
||||
fwidth = 0U;
|
||||
}
|
||||
|
||||
// rescale the float value
|
||||
if (expval) {
|
||||
value /= conv.F;
|
||||
}
|
||||
|
||||
// output the floating part
|
||||
const size_t start_idx = idx;
|
||||
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
|
||||
|
||||
// output the exponent part
|
||||
if (minwidth) {
|
||||
// output the exponential symbol
|
||||
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
|
||||
// output the exponent value
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
|
||||
// might need to right-pad spaces
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
#endif // PRINTF_SUPPORT_EXPONENTIAL
|
||||
#endif // PRINTF_SUPPORT_FLOAT
|
||||
|
||||
|
||||
// internal vsnprintf
|
||||
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
|
||||
{
|
||||
unsigned int flags, width, precision, n;
|
||||
size_t idx = 0U;
|
||||
|
||||
if (!buffer) {
|
||||
// use null output function
|
||||
out = _out_null;
|
||||
}
|
||||
|
||||
while (*format)
|
||||
{
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
if (*format != '%') {
|
||||
// no
|
||||
out(*format, buffer, idx++, maxlen);
|
||||
format++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// yes, evaluate it
|
||||
format++;
|
||||
}
|
||||
|
||||
// evaluate flags
|
||||
flags = 0U;
|
||||
do {
|
||||
switch (*format) {
|
||||
case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
|
||||
case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
|
||||
case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
|
||||
case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
|
||||
case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
|
||||
default : n = 0U; break;
|
||||
}
|
||||
} while (n);
|
||||
|
||||
// evaluate width field
|
||||
width = 0U;
|
||||
if (_is_digit(*format)) {
|
||||
width = _atoi(&format);
|
||||
}
|
||||
else if (*format == '*') {
|
||||
const int w = va_arg(va, int);
|
||||
if (w < 0) {
|
||||
flags |= FLAGS_LEFT; // reverse padding
|
||||
width = (unsigned int)-w;
|
||||
}
|
||||
else {
|
||||
width = (unsigned int)w;
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
// evaluate precision field
|
||||
precision = 0U;
|
||||
if (*format == '.') {
|
||||
flags |= FLAGS_PRECISION;
|
||||
format++;
|
||||
if (_is_digit(*format)) {
|
||||
precision = _atoi(&format);
|
||||
}
|
||||
else if (*format == '*') {
|
||||
const int prec = (int)va_arg(va, int);
|
||||
precision = prec > 0 ? (unsigned int)prec : 0U;
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate length field
|
||||
switch (*format) {
|
||||
case 'l' :
|
||||
flags |= FLAGS_LONG;
|
||||
format++;
|
||||
if (*format == 'l') {
|
||||
flags |= FLAGS_LONG_LONG;
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
case 'h' :
|
||||
flags |= FLAGS_SHORT;
|
||||
format++;
|
||||
if (*format == 'h') {
|
||||
flags |= FLAGS_CHAR;
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
|
||||
case 't' :
|
||||
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
#endif
|
||||
case 'j' :
|
||||
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
case 'z' :
|
||||
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
switch (*format) {
|
||||
case 'd' :
|
||||
case 'i' :
|
||||
case 'u' :
|
||||
case 'x' :
|
||||
case 'X' :
|
||||
case 'o' :
|
||||
case 'b' : {
|
||||
// set the base
|
||||
unsigned int base;
|
||||
if (*format == 'x' || *format == 'X') {
|
||||
base = 16U;
|
||||
}
|
||||
else if (*format == 'o') {
|
||||
base = 8U;
|
||||
}
|
||||
else if (*format == 'b') {
|
||||
base = 2U;
|
||||
}
|
||||
else {
|
||||
base = 10U;
|
||||
flags &= ~FLAGS_HASH; // no hash for dec format
|
||||
}
|
||||
// uppercase
|
||||
if (*format == 'X') {
|
||||
flags |= FLAGS_UPPERCASE;
|
||||
}
|
||||
|
||||
// no plus or space flag for u, x, X, o, b
|
||||
if ((*format != 'i') && (*format != 'd')) {
|
||||
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
|
||||
}
|
||||
|
||||
// ignore '0' flag when precision is given
|
||||
if (flags & FLAGS_PRECISION) {
|
||||
flags &= ~FLAGS_ZEROPAD;
|
||||
}
|
||||
|
||||
// convert the integer
|
||||
if ((*format == 'i') || (*format == 'd')) {
|
||||
// signed
|
||||
if (flags & FLAGS_LONG_LONG) {
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
const long long value = va_arg(va, long long);
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
||||
#endif
|
||||
}
|
||||
else if (flags & FLAGS_LONG) {
|
||||
const long value = va_arg(va, long);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
||||
}
|
||||
else {
|
||||
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// unsigned
|
||||
if (flags & FLAGS_LONG_LONG) {
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
|
||||
#endif
|
||||
}
|
||||
else if (flags & FLAGS_LONG) {
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
|
||||
}
|
||||
else {
|
||||
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
case 'f' :
|
||||
case 'F' :
|
||||
if (*format == 'F') flags |= FLAGS_UPPERCASE;
|
||||
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
||||
format++;
|
||||
break;
|
||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'g':
|
||||
case 'G':
|
||||
if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
|
||||
if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
|
||||
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
||||
format++;
|
||||
break;
|
||||
#endif // PRINTF_SUPPORT_EXPONENTIAL
|
||||
#endif // PRINTF_SUPPORT_FLOAT
|
||||
case 'c' : {
|
||||
unsigned int l = 1U;
|
||||
// pre padding
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
// char output
|
||||
out((char)va_arg(va, int), buffer, idx++, maxlen);
|
||||
// post padding
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 's' : {
|
||||
const char* p = va_arg(va, char*);
|
||||
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
|
||||
// pre padding
|
||||
if (flags & FLAGS_PRECISION) {
|
||||
l = (l < precision ? l : precision);
|
||||
}
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
// string output
|
||||
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
|
||||
out(*(p++), buffer, idx++, maxlen);
|
||||
}
|
||||
// post padding
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p' : {
|
||||
width = sizeof(void*) * 2U;
|
||||
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
|
||||
if (is_ll) {
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
}
|
||||
#endif
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '%' :
|
||||
out('%', buffer, idx++, maxlen);
|
||||
format++;
|
||||
break;
|
||||
|
||||
default :
|
||||
out(*format, buffer, idx++, maxlen);
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// termination
|
||||
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return (int)idx;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int printf_(const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
char buffer[1];
|
||||
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int sprintf_(char* buffer, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int snprintf_(char* buffer, size_t count, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int vprintf_(const char* format, va_list va)
|
||||
{
|
||||
char buffer[1];
|
||||
return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
|
||||
}
|
||||
|
||||
|
||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
|
||||
{
|
||||
return _vsnprintf(_out_buffer, buffer, count, format, va);
|
||||
}
|
||||
|
||||
|
||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
const out_fct_wrap_type out_fct_wrap = { out, arg };
|
||||
const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
|
@ -34,6 +34,7 @@ static enum {
|
|||
WRITING,
|
||||
ERROR,
|
||||
DONE,
|
||||
OFF,
|
||||
} rgb_mode;
|
||||
|
||||
static void rgb_write(uint8_t value, uint8_t addr) {
|
||||
|
@ -86,4 +87,8 @@ void rgb_mode_error(void) {
|
|||
|
||||
void rgb_mode_done(void) {
|
||||
rgb_switch_mode(DONE, 8, 8, 2, 3, 0x14/4, 0xff/4, 0x44/4);
|
||||
}
|
||||
|
||||
void rgb_mode_off(void) {
|
||||
rgb_switch_mode(OFF, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
918
sw/src/spi.c
918
sw/src/spi.c
File diff suppressed because it is too large
Load Diff
216
sw/src/tester.c
216
sw/src/tester.c
|
@ -1,5 +1,217 @@
|
|||
#include <tester.h>
|
||||
#include <usb-cdc.h>
|
||||
#include <spi.h>
|
||||
#include <usb.h>
|
||||
#include <fomu/csr.h>
|
||||
#include <time.h>
|
||||
#include <rgb.h>
|
||||
|
||||
void tester_poll(void) {
|
||||
return;
|
||||
static uint32_t test_spi(void)
|
||||
{
|
||||
uint8_t test_buffer[64];
|
||||
uint8_t compare_buffer[sizeof(test_buffer)];
|
||||
unsigned int i;
|
||||
int errors = 0;
|
||||
|
||||
struct spi_id id = spiId();
|
||||
spiSetType(ST_QUAD);
|
||||
put_string("SPI: Manufacturer ");
|
||||
put_hex_byte(id.manufacturer_id);
|
||||
put_string(" / ");
|
||||
|
||||
put_string("Device ID ");
|
||||
put_hex_byte(id.device_id);
|
||||
put_string(" / ");
|
||||
|
||||
put_string("Capacity ");
|
||||
put_hex_byte(id.memory_type);
|
||||
put_char(' ');
|
||||
put_hex_byte(id.memory_size);
|
||||
put_string(" / ");
|
||||
|
||||
put_string("Serial ");
|
||||
put_hex(*((uint32_t *)id.serial));
|
||||
put_string(" / ");
|
||||
|
||||
for (i = 0; i < sizeof(test_buffer); i++)
|
||||
{
|
||||
test_buffer[i] = (i ^ 0x9d) ^ (i << 5);
|
||||
}
|
||||
spiWrite(0, test_buffer, sizeof(test_buffer));
|
||||
|
||||
for (i = 0; i < sizeof(compare_buffer); i++)
|
||||
{
|
||||
compare_buffer[i] = 0;
|
||||
}
|
||||
spiRead(0, compare_buffer, sizeof(compare_buffer));
|
||||
|
||||
for (i = 0; i < sizeof(compare_buffer); i++)
|
||||
{
|
||||
if (test_buffer[i] != compare_buffer[i])
|
||||
{
|
||||
put_string("E@");
|
||||
put_hex_byte(i);
|
||||
put_char(':');
|
||||
put_hex_byte(test_buffer[i]);
|
||||
put_char('!');
|
||||
put_hex_byte(compare_buffer[i]);
|
||||
put_char(' ');
|
||||
// printf("SPI: Offset %d Expected %02x Got %02x\n", i, test_buffer[i], compare_buffer[i]);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
if (!errors)
|
||||
{
|
||||
put_string("Pass\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
put_string("FAIL\n");
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static uint32_t test_one_pad(uint8_t src, uint8_t dest)
|
||||
{
|
||||
unsigned int loops;
|
||||
unsigned int matches = 0;
|
||||
const unsigned int loop_max = 10;
|
||||
|
||||
put_char('0' + src);
|
||||
put_char('>');
|
||||
put_char('0' + dest);
|
||||
put_char(':');
|
||||
for (loops = 0; loops < loop_max; loops++)
|
||||
{
|
||||
// Set pin 2 as output, and pin 0 as input, and see if it loops back.
|
||||
touch_oe_write((1 << src) | (0 << dest));
|
||||
touch_o_write((loops & 1) << src);
|
||||
if ((loops & 1) == !!((touch_i_read() & (1 << dest))))
|
||||
matches++;
|
||||
}
|
||||
if (matches == loop_max)
|
||||
{
|
||||
put_string("OK ");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
put_string("FAIL(");
|
||||
put_hex_byte(loop_max);
|
||||
put_char('!');
|
||||
put_hex_byte(matches);
|
||||
put_string(") ");
|
||||
return (loop_max - matches);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t test_touch(void)
|
||||
{
|
||||
uint32_t error_count = 0;
|
||||
|
||||
put_string("TOUCH: ");
|
||||
|
||||
error_count += test_one_pad(0, 2);
|
||||
error_count += test_one_pad(0, 3);
|
||||
error_count += test_one_pad(2, 0);
|
||||
error_count += test_one_pad(2, 3);
|
||||
error_count += test_one_pad(3, 0);
|
||||
error_count += test_one_pad(3, 2);
|
||||
|
||||
if (error_count)
|
||||
put_string("FAIL\n");
|
||||
else
|
||||
put_string("Pass\n");
|
||||
return error_count;
|
||||
}
|
||||
|
||||
static const char color_names[] = {'B', 'R', 'G'};
|
||||
|
||||
static uint32_t test_one_color(int color)
|
||||
{
|
||||
uint32_t pulses_per_second;
|
||||
uint32_t sent_pulses;
|
||||
uint32_t detected_pulses;
|
||||
uint32_t high_value;
|
||||
|
||||
rgb_bypass_write(1 << color);
|
||||
rgb_mode_off();
|
||||
|
||||
// Amount of time it's off (900 us)
|
||||
rgb_duty_write(SYSTEM_CLOCK_FREQUENCY / 10000 * 9);
|
||||
|
||||
// Total amount of time for one pulse (1000 us or 1 ms)
|
||||
rgb_pulse_write(SYSTEM_CLOCK_FREQUENCY / 10000 * 10);
|
||||
|
||||
put_string("RGB");
|
||||
put_char(color_names[color]);
|
||||
put_string(": ");
|
||||
msleep(100);
|
||||
|
||||
rgb_pulse_write(SYSTEM_CLOCK_FREQUENCY / 1000 * 1);
|
||||
pulses_per_second = rgb_pulse_read();
|
||||
high_value = rgb_duty_read();
|
||||
sent_pulses = rgb_sent_pulses_read();
|
||||
detected_pulses = rgb_detected_pulses_read();
|
||||
|
||||
put_hex(pulses_per_second);
|
||||
put_string(" / ");
|
||||
put_hex(high_value);
|
||||
put_string(" / ");
|
||||
put_hex(sent_pulses);
|
||||
put_string(" / ");
|
||||
put_hex(detected_pulses);
|
||||
put_string(" / ");
|
||||
rgb_bypass_write(0);
|
||||
|
||||
uint32_t ratio = ((detected_pulses * 100) / sent_pulses);
|
||||
put_string("Ratio: 0x");
|
||||
put_hex_byte(ratio);
|
||||
put_string(" / ");
|
||||
if (ratio > 60)
|
||||
{
|
||||
put_string("Pass\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
put_string("FAIL\n");
|
||||
return 1 + ratio;
|
||||
}
|
||||
|
||||
static uint32_t test_led(void)
|
||||
{
|
||||
uint32_t error_count = 0;
|
||||
|
||||
touch_oe_write(touch_oe_read() & ~(1 << 1));
|
||||
// touch_oe_write(touch_oe_read() | (1 << 1));
|
||||
// touch_o_write(touch_o_read() & ~(1 << 1));
|
||||
|
||||
error_count += test_one_color(0);
|
||||
error_count += test_one_color(1);
|
||||
error_count += test_one_color(2);
|
||||
return error_count;
|
||||
}
|
||||
|
||||
void tester_poll(void)
|
||||
{
|
||||
int error_count = 0;
|
||||
put_char('\n');
|
||||
flush_serial();
|
||||
put_string("\nFomu Tester " GIT_VERSION "\n");
|
||||
error_count += test_spi();
|
||||
error_count += test_led();
|
||||
error_count += test_touch();
|
||||
|
||||
put_string("FOMU: (0x");
|
||||
put_hex(error_count);
|
||||
put_string(" errors) ");
|
||||
if (error_count)
|
||||
put_string("FAIL!\n");
|
||||
else
|
||||
put_string("ALL_PASS\n");
|
||||
while (1)
|
||||
{
|
||||
usb_poll();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
#include <usb.h>
|
||||
#include <usb-cdc.h>
|
||||
#include <fomu/csr.h>
|
||||
|
||||
static int connected = 0;
|
||||
struct str_bfr
|
||||
{
|
||||
uint8_t bfr_contents;
|
||||
} str_bfr;
|
||||
|
||||
int cdc_connected(void)
|
||||
{
|
||||
|
@ -10,4 +16,83 @@ int cdc_connected(void)
|
|||
void cdc_set_connected(int is_connected)
|
||||
{
|
||||
connected = is_connected;
|
||||
}
|
||||
|
||||
void _putchar(char character)
|
||||
{
|
||||
if (character == '\n')
|
||||
_putchar('\r');
|
||||
|
||||
// Wait for buffer to be empty
|
||||
while (usb_ep_2_in_respond_read() == EPF_ACK)
|
||||
;
|
||||
usb_ep_2_in_ibuf_head_write(character);
|
||||
usb_ep_2_in_respond_write(EPF_ACK);
|
||||
}
|
||||
|
||||
void flush_serial(void)
|
||||
{
|
||||
if (!str_bfr.bfr_contents)
|
||||
return;
|
||||
|
||||
usb_ep_2_in_respond_write(EPF_ACK);
|
||||
// Wait for buffer to be empty
|
||||
while (usb_ep_2_in_respond_read() == EPF_ACK)
|
||||
;
|
||||
str_bfr.bfr_contents = 0;
|
||||
}
|
||||
|
||||
void add_char_to_buffer(char character)
|
||||
{
|
||||
while (usb_ep_2_in_respond_read() == EPF_ACK)
|
||||
;
|
||||
usb_ep_2_in_ibuf_head_write(character);
|
||||
str_bfr.bfr_contents++;
|
||||
if (str_bfr.bfr_contents >= 64)
|
||||
flush_serial();
|
||||
}
|
||||
|
||||
void put_string(const char *str)
|
||||
{
|
||||
while (*str != '\0')
|
||||
{
|
||||
if (*str == '\n')
|
||||
add_char_to_buffer('\r');
|
||||
add_char_to_buffer(*str);
|
||||
str++;
|
||||
}
|
||||
flush_serial();
|
||||
}
|
||||
|
||||
void put_hex(uint32_t val)
|
||||
{
|
||||
int num_nibbles = sizeof(val) * 2;
|
||||
|
||||
do
|
||||
{
|
||||
char v = '0' + (((val >> (num_nibbles - 1) * 4)) & 0x0f);
|
||||
if (v > '9')
|
||||
v += 'a' - ('9'+1);
|
||||
put_char(v);
|
||||
} while (--num_nibbles);
|
||||
}
|
||||
|
||||
void put_hex_byte(uint8_t val)
|
||||
{
|
||||
int num_nibbles = sizeof(val) * 2;
|
||||
|
||||
do
|
||||
{
|
||||
char v = '0' + (((val >> (num_nibbles - 1) * 4)) & 0x0f);
|
||||
if (v > '9')
|
||||
v += 'a' - ('9'+1);
|
||||
put_char(v);
|
||||
} while (--num_nibbles);
|
||||
}
|
||||
|
||||
void put_char(char character)
|
||||
{
|
||||
if (character == '\n')
|
||||
add_char_to_buffer('\r');
|
||||
add_char_to_buffer(character);
|
||||
}
|
|
@ -60,7 +60,7 @@
|
|||
static const uint8_t device_descriptor[] = {
|
||||
18, // bLength
|
||||
1, // bDescriptorType
|
||||
0x01, 0x02, // bcdUSB
|
||||
0x10, 0x01, // bcdUSB
|
||||
USB_CLASS_CDC, // bDeviceClass
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
|
|
|
@ -5,40 +5,27 @@
|
|||
|
||||
#ifdef CSR_USB_EP_0_OUT_EV_PENDING_ADDR
|
||||
|
||||
#define EP0OUT_BUFFERS 4
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t volatile usb_ep0out_buffer_len[EP0OUT_BUFFERS];
|
||||
static uint8_t volatile usb_ep0out_buffer[EP0OUT_BUFFERS][256];
|
||||
static uint8_t volatile usb_ep0out_last_tok[EP0OUT_BUFFERS];
|
||||
static volatile uint8_t usb_ep0out_wr_ptr;
|
||||
static volatile uint8_t usb_ep0out_rd_ptr;
|
||||
static const int max_byte_length = 64;
|
||||
|
||||
#define EP2OUT_BUFFERS 4
|
||||
#define EP0OUT_BUFFER_SIZE 256
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t volatile usb_ep0out_buffer[64 + 2];
|
||||
static int wait_reply;
|
||||
static int wait_type;
|
||||
|
||||
#define EP2OUT_BUFFER_SIZE 256
|
||||
static uint8_t volatile usb_ep2out_buffer_len[EP2OUT_BUFFERS];
|
||||
static uint8_t volatile usb_ep2out_buffer[EP2OUT_BUFFERS][EP2OUT_BUFFER_SIZE];
|
||||
static volatile uint8_t usb_ep2out_wr_ptr;
|
||||
static volatile uint8_t usb_ep2out_rd_ptr;
|
||||
|
||||
static const uint8_t * volatile current_data;
|
||||
static volatile int current_length;
|
||||
static volatile int data_offset;
|
||||
static volatile int data_to_send;
|
||||
static int next_packet_is_empty;
|
||||
|
||||
// Note that our PIDs are only bits 2 and 3 of the token,
|
||||
// since all other bits are effectively redundant at this point.
|
||||
enum USB_PID {
|
||||
USB_PID_OUT = 0,
|
||||
USB_PID_SOF = 1,
|
||||
USB_PID_IN = 2,
|
||||
USB_PID_SETUP = 3,
|
||||
};
|
||||
|
||||
enum epfifo_response {
|
||||
EPF_ACK = 0,
|
||||
EPF_NAK = 1,
|
||||
EPF_NONE = 2,
|
||||
EPF_STALL = 3,
|
||||
};
|
||||
|
||||
#define USB_EV_ERROR 1
|
||||
#define USB_EV_PACKET 2
|
||||
|
||||
void usb_idle(void) {
|
||||
usb_ep_0_out_ev_enable_write(0);
|
||||
usb_ep_0_in_ev_enable_write(0);
|
||||
|
@ -60,14 +47,22 @@ void usb_disconnect(void) {
|
|||
}
|
||||
|
||||
void usb_connect(void) {
|
||||
|
||||
usb_ep_0_out_ev_pending_write(usb_ep_0_out_ev_enable_read());
|
||||
usb_ep_0_in_ev_pending_write(usb_ep_0_in_ev_pending_read());
|
||||
usb_ep_0_out_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
|
||||
usb_ep_0_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
|
||||
|
||||
usb_ep_1_in_ev_pending_write(usb_ep_1_in_ev_enable_read());
|
||||
usb_ep_1_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
|
||||
|
||||
usb_ep_2_out_ev_pending_write(usb_ep_2_out_ev_enable_read());
|
||||
usb_ep_2_in_ev_pending_write(usb_ep_2_in_ev_pending_read());
|
||||
usb_ep_2_out_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
|
||||
usb_ep_2_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
|
||||
|
||||
// Accept incoming data by default.
|
||||
usb_ep_0_out_respond_write(EPF_ACK);
|
||||
usb_ep_2_out_respond_write(EPF_ACK);
|
||||
|
||||
// Reject outgoing data, since we have none to give yet.
|
||||
usb_ep_0_in_respond_write(EPF_NAK);
|
||||
|
@ -78,8 +73,8 @@ void usb_connect(void) {
|
|||
}
|
||||
|
||||
void usb_init(void) {
|
||||
usb_ep0out_wr_ptr = 0;
|
||||
usb_ep0out_rd_ptr = 0;
|
||||
// usb_ep0out_wr_ptr = 0;
|
||||
// usb_ep0out_rd_ptr = 0;
|
||||
usb_pullup_out_write(0);
|
||||
return;
|
||||
}
|
||||
|
@ -135,8 +130,7 @@ static void process_tx(void) {
|
|||
}
|
||||
|
||||
void usb_send(const void *data, int total_count) {
|
||||
|
||||
while ((current_length || current_data))// && usb_ep_0_in_respond_read() != EPF_NAK)
|
||||
while ((current_length || current_data) && (usb_ep_0_in_respond_read() != EPF_NAK))
|
||||
;
|
||||
current_data = (uint8_t *)data;
|
||||
current_length = total_count;
|
||||
|
@ -153,46 +147,110 @@ void usb_wait_for_send_done(void) {
|
|||
}
|
||||
|
||||
void usb_isr(void) {
|
||||
uint8_t ep0o_pending = usb_ep_0_out_ev_pending_read();
|
||||
uint8_t ep0i_pending = usb_ep_0_in_ev_pending_read();
|
||||
uint8_t ep0out_pending = usb_ep_0_out_ev_pending_read();
|
||||
uint8_t ep0in_pending = usb_ep_0_in_ev_pending_read();
|
||||
uint8_t ep1in_pending = usb_ep_1_in_ev_pending_read();
|
||||
uint8_t ep2in_pending = usb_ep_2_in_ev_pending_read();
|
||||
uint8_t ep2out_pending = usb_ep_2_out_ev_pending_read();
|
||||
|
||||
// We just got an "IN" token. Send data if we have it.
|
||||
if (ep0in_pending) {
|
||||
if (wait_reply == 2) {
|
||||
wait_reply--;
|
||||
if (!wait_type) {
|
||||
wait_type = 1;
|
||||
}
|
||||
}
|
||||
else if (wait_reply == 1) {
|
||||
if (wait_type == 2) {
|
||||
current_data = NULL;
|
||||
current_length = 0;
|
||||
}
|
||||
wait_type = 0;
|
||||
}
|
||||
usb_ep_0_in_respond_write(EPF_NAK);
|
||||
usb_ep_0_in_ev_pending_write(ep0in_pending);
|
||||
}
|
||||
|
||||
if (ep1in_pending) {
|
||||
usb_ep_1_in_respond_write(EPF_NAK);
|
||||
usb_ep_1_in_ev_pending_write(ep1in_pending);
|
||||
}
|
||||
|
||||
if (ep2in_pending) {
|
||||
usb_ep_2_in_respond_write(EPF_NAK);
|
||||
usb_ep_2_in_ev_pending_write(ep2in_pending);
|
||||
}
|
||||
|
||||
if (ep2out_pending) {
|
||||
#ifdef LOOPBACK_TEST
|
||||
volatile uint8_t * obuf = usb_ep2out_buffer[usb_ep2out_wr_ptr];
|
||||
int sz = 0;
|
||||
|
||||
if (wait_reply == 2) {
|
||||
wait_reply--;
|
||||
wait_type = 2;
|
||||
}
|
||||
else if (wait_reply == 1) {
|
||||
wait_reply--;
|
||||
}
|
||||
while (!usb_ep_2_out_obuf_empty_read()) {
|
||||
if (sz < EP2OUT_BUFFER_SIZE)
|
||||
obuf[sz++] = usb_ep_2_out_obuf_head_read() + 1;
|
||||
usb_ep_2_out_obuf_head_write(0);
|
||||
}
|
||||
if (sz > 2) {
|
||||
usb_ep2out_buffer_len[usb_ep2out_wr_ptr] = sz - 2; /* Strip off CRC16 */
|
||||
usb_ep2out_wr_ptr = (usb_ep2out_wr_ptr + 1) & (EP2OUT_BUFFERS-1);
|
||||
}
|
||||
#else // !LOOPBACK_TEST
|
||||
while (!usb_ep_2_out_obuf_empty_read()) {
|
||||
usb_ep_2_out_obuf_head_write(0);
|
||||
}
|
||||
#endif
|
||||
usb_ep_2_out_respond_write(EPF_ACK);
|
||||
usb_ep_2_out_ev_pending_write(ep2out_pending);
|
||||
}
|
||||
|
||||
// We got an OUT or a SETUP packet. Copy it to usb_ep0out_buffer
|
||||
// and clear the "pending" bit.
|
||||
if (ep0o_pending) {
|
||||
uint8_t last_tok = usb_ep_0_out_last_tok_read();
|
||||
|
||||
int byte_count = 0;
|
||||
usb_ep0out_last_tok[usb_ep0out_wr_ptr] = last_tok;
|
||||
volatile uint8_t * obuf = usb_ep0out_buffer[usb_ep0out_wr_ptr];
|
||||
if (!usb_ep_0_out_obuf_empty_read()) {
|
||||
while (!usb_ep_0_out_obuf_empty_read()) {
|
||||
obuf[byte_count++] = usb_ep_0_out_obuf_head_read();
|
||||
usb_ep_0_out_obuf_head_write(0);
|
||||
}
|
||||
if (ep0out_pending) {
|
||||
unsigned int byte_count = 0;
|
||||
for (byte_count = 0; byte_count < sizeof(usb_ep0out_buffer); byte_count++)
|
||||
usb_ep0out_buffer[byte_count] = '\0';
|
||||
|
||||
byte_count = 0;
|
||||
while (!usb_ep_0_out_obuf_empty_read()) {
|
||||
uint8_t byte = usb_ep_0_out_obuf_head_read();
|
||||
usb_ep_0_out_obuf_head_write(0);
|
||||
usb_ep0out_buffer[byte_count++] = byte;
|
||||
}
|
||||
|
||||
if (byte_count >= 2)
|
||||
usb_ep0out_buffer_len[usb_ep0out_wr_ptr] = byte_count - 2 /* Strip off CRC16 */;
|
||||
usb_ep0out_wr_ptr = (usb_ep0out_wr_ptr + 1) & (EP0OUT_BUFFERS-1);
|
||||
|
||||
if (last_tok == USB_PID_SETUP) {
|
||||
if (byte_count >= 2) {
|
||||
volatile void *setup_buffer = usb_ep0out_buffer;
|
||||
usb_ep_0_in_dtb_write(1);
|
||||
data_offset = 0;
|
||||
current_length = 0;
|
||||
current_data = NULL;
|
||||
byte_count -= 2;
|
||||
// XXX TERRIBLE HACK!
|
||||
// Because the epfifo backend doesn't have any concept of packet boundaries,
|
||||
// sometimes one or two of the bytes from the CRC on the "ACK" from the previous
|
||||
// "Get Descriptor" will be stuck on the front of this request.
|
||||
// This can happen if, for example, we get the OUT from that and the OUT from
|
||||
// the subsequent SETUP packet without first handling that.
|
||||
// Since all SETUP packets are 8 bytes (in this tester), we'll simply clamp the
|
||||
// SETUP data packet to be the last 8 bytes received (minus the 2-byte CRC16).
|
||||
// This is horrible and should be fixed in hardware.
|
||||
if (byte_count > 8)
|
||||
setup_buffer += byte_count - 8;
|
||||
wait_reply = usb_setup((const struct usb_setup_request *)setup_buffer);
|
||||
}
|
||||
|
||||
usb_ep_0_out_ev_pending_write(ep0o_pending);
|
||||
usb_ep_0_out_ev_pending_write(ep0out_pending);
|
||||
usb_ep_0_out_respond_write(EPF_ACK);
|
||||
}
|
||||
|
||||
// We just got an "IN" token. Send data if we have it.
|
||||
if (ep0i_pending) {
|
||||
usb_ep_0_in_respond_write(EPF_NAK);
|
||||
usb_ep_0_in_ev_pending_write(ep0i_pending);
|
||||
}
|
||||
|
||||
return;
|
||||
process_tx();
|
||||
}
|
||||
|
||||
void usb_ack_in(void) {
|
||||
|
@ -212,6 +270,7 @@ void usb_err(void) {
|
|||
usb_ep_0_in_respond_write(EPF_STALL);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int usb_recv(void *buffer, unsigned int buffer_len) {
|
||||
|
||||
// Set the OUT response to ACK, since we are in a position to receive data now.
|
||||
|
@ -232,23 +291,25 @@ int usb_recv(void *buffer, unsigned int buffer_len) {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void usb_poll(void) {
|
||||
// If some data was received, then process it.
|
||||
while (usb_ep0out_rd_ptr != usb_ep0out_wr_ptr) {
|
||||
const struct usb_setup_request *request = (const struct usb_setup_request *)(usb_ep0out_buffer[usb_ep0out_rd_ptr]);
|
||||
// uint8_t len = usb_ep0out_buffer_len[usb_ep0out_rd_ptr];
|
||||
uint8_t last_tok = usb_ep0out_last_tok[usb_ep0out_rd_ptr];
|
||||
|
||||
// usb_ep0out_buffer_len[usb_ep0out_rd_ptr] = 0;
|
||||
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
|
||||
|
||||
if (last_tok == USB_PID_SETUP) {
|
||||
usb_setup(request);
|
||||
}
|
||||
}
|
||||
|
||||
process_tx();
|
||||
#ifdef LOOPBACK_TEST
|
||||
if (usb_ep2out_rd_ptr != usb_ep2out_wr_ptr) {
|
||||
volatile uint8_t *buf = usb_ep2out_buffer[usb_ep2out_rd_ptr];
|
||||
unsigned int len = usb_ep2out_buffer_len[usb_ep2out_rd_ptr];
|
||||
unsigned int i;
|
||||
while (usb_ep_2_in_respond_read() == EPF_ACK) {
|
||||
;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
usb_ep_2_in_ibuf_head_write(buf[i]);
|
||||
}
|
||||
usb_ep_2_in_respond_write(EPF_ACK);
|
||||
usb_ep2out_rd_ptr = (usb_ep2out_rd_ptr + 1) & (EP2OUT_BUFFERS-1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* CSR_USB_EP_0_OUT_EV_PENDING_ADDR */
|
|
@ -8,16 +8,30 @@
|
|||
static uint8_t reply_buffer[8];
|
||||
static uint8_t usb_configuration = 0;
|
||||
|
||||
void usb_setup(const struct usb_setup_request *setup)
|
||||
int usb_setup(const struct usb_setup_request *setup)
|
||||
{
|
||||
const uint8_t *data = NULL;
|
||||
uint32_t datalen = 0;
|
||||
const usb_descriptor_list_t *list;
|
||||
uint32_t max_length = setup->wLength;//((setup->wLength >> 8) & 0xff) | ((setup->wLength << 8) & 0xff00);
|
||||
|
||||
switch (setup->wRequestAndType)
|
||||
{
|
||||
|
||||
// case 0x21a1: // Get Line Coding
|
||||
// reply_buffer[0] = 0x80;
|
||||
// reply_buffer[1] = 0x25;
|
||||
// reply_buffer[2] = 0x00;
|
||||
// reply_buffer[3] = 0x00;
|
||||
// reply_buffer[4] = 0x00;
|
||||
// reply_buffer[5] = 0x00;
|
||||
// reply_buffer[6] = 0x08;
|
||||
// data = reply_buffer;
|
||||
// datalen = 7;
|
||||
// break;
|
||||
|
||||
case 0x2021: // Set Line Coding
|
||||
case 0x20A1: // Set Line Coding
|
||||
break;
|
||||
|
||||
case 0x2221: // Set control line state
|
||||
|
@ -49,7 +63,7 @@ void usb_setup(const struct usb_setup_request *setup)
|
|||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
reply_buffer[0] = 0;
|
||||
reply_buffer[1] = 0;
|
||||
|
@ -64,7 +78,7 @@ void usb_setup(const struct usb_setup_request *setup)
|
|||
{
|
||||
// TODO: do we need to handle IN vs OUT here?
|
||||
usb_err();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -73,7 +87,7 @@ void usb_setup(const struct usb_setup_request *setup)
|
|||
{
|
||||
// TODO: do we need to handle IN vs OUT here?
|
||||
usb_err();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
// XXX: Should we set the stall bit?
|
||||
// USB->DIEP0CTL |= USB_DIEP_CTL_STALL;
|
||||
|
@ -104,20 +118,20 @@ void usb_setup(const struct usb_setup_request *setup)
|
|||
}
|
||||
}
|
||||
usb_err();
|
||||
return;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
usb_err();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
send:
|
||||
if (data && datalen) {
|
||||
if (datalen > setup->wLength)
|
||||
datalen = setup->wLength;
|
||||
if (datalen > max_length)
|
||||
datalen = max_length;
|
||||
usb_send(data, datalen);
|
||||
}
|
||||
else
|
||||
usb_ack_in();
|
||||
return;
|
||||
return 2;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue