Compare commits
127 Commits
Author | SHA1 | Date |
---|---|---|
Sean Cross | c0df98f66e | |
Sean Cross | 6d9028f505 | |
Sean Cross | 7ee97214e6 | |
Sean Cross | 4b6ad47705 | |
Sean Cross | 91a23d808c | |
Sean Cross | 2a7e431947 | |
Sean Cross | a9a5f0cc0d | |
Sean Cross | d6ae51f69b | |
Sean Cross | 2437e8b5c1 | |
Sean Cross | 6435aec3db | |
Sean Cross | b40a9bad43 | |
Sean Cross | b5f17acdf6 | |
Sean Cross | db65ccc199 | |
Sean Cross | fd76536351 | |
Sean Cross | eaa0d63ccb | |
Sean Cross | 7d091a72b9 | |
Sean Cross | 7bcc8529d6 | |
Sean Cross | ac349ce1a1 | |
Sean Cross | cbf4958236 | |
Sean Cross | 2b0575889f | |
Sean Cross | c92af747cd | |
Sean Cross | d72925fdeb | |
Sean Cross | 4c3f0f2402 | |
Sean Cross | 1660681d38 | |
Sean Cross | c33d86adb9 | |
Sean Cross | 85b6d75882 | |
Sean Cross | dbc0f81715 | |
Sean Cross | 851e54e70c | |
Sean Cross | 85bdc7991c | |
Sean Cross | a3c05bd19e | |
Sean Cross | 33aec0876b | |
Sean Cross | 8599ec7007 | |
Sean Cross | 0e720d5acc | |
Sean Cross | 2fd01b8303 | |
Sean Cross | 0a4c73a63a | |
Sean Cross | 6595eb1ef1 | |
Sean Cross | 7191c12490 | |
Sean Cross | a3298226e5 | |
Sean Cross | b0afd2b0ae | |
Sean Cross | 3d6acaf51e | |
Sean Cross | 7210ee219d | |
Sean Cross | 2139317530 | |
Sean Cross | dd9cdfa7a3 | |
Sean Cross | 23b9962067 | |
Sean Cross | fa690d63ed | |
Sean Cross | 4f0507fc77 | |
Sean Cross | f3d779787b | |
Sean Cross | b09333f023 | |
Sean Cross | 03327067ff | |
Sean Cross | c7632ae8bd | |
Sean Cross | 4aa3861c03 | |
Sean Cross | ff4774ebc3 | |
Sean Cross | 9909b3bbdb | |
Sean Cross | f5fd282d61 | |
Sean Cross | 5bcd6c44fb | |
Sean Cross | d64f88d995 | |
Sean Cross | c180c1b1b8 | |
Sean Cross | a552d1eb91 | |
Sean Cross | 2d7c7794f5 | |
Sean Cross | 0c6e444789 | |
Sean Cross | 8aed600cd6 | |
Sean Cross | 6638801886 | |
Sean Cross | 8fb6b5977b | |
Sean Cross | 69f7b5d836 | |
Sean Cross | c98017cbc9 | |
Sean Cross | 13360015db | |
Sean Cross | d603113b6f | |
Sean Cross | 44ee19c8b4 | |
Sean Cross | f34601df98 | |
Sean Cross | 3df59a866d | |
Sean Cross | c0842737bf | |
Sean Cross | ce071dac67 | |
Sean Cross | 7f30d7f79b | |
Sean Cross | 380a4f1fa2 | |
Sean Cross | 73176b65de | |
Sean Cross | c78e9ab214 | |
Sean Cross | ffd0285613 | |
Sean Cross | 1c38c58a6f | |
Sean Cross | 350497924e | |
Sean Cross | 74ec6be245 | |
Sean Cross | 1c8634e954 | |
Sean Cross | 8fe27d9371 | |
Sean Cross | e4af98b4aa | |
Sean Cross | d812378c4d | |
Sean Cross | 84d4b40897 | |
Sean Cross | b06cd3b807 | |
Sean Cross | d18e0cba0d | |
Sean Cross | a897c7f2fa | |
Sean Cross | 6a147c4333 | |
Sean Cross | 2ac79e45e9 | |
Sean Cross | 1048cbf999 | |
Sean Cross | fae65117cd | |
Sean Cross | 1cb67f8f8d | |
Sean Cross | cad2ae01d7 | |
Sean Cross | dd7839cc68 | |
Sean Cross | 05480670c5 | |
Sean Cross | 6ca4b6a398 | |
Sean Cross | 5fbd0fc667 | |
Sean Cross | d42418d62a | |
Sean Cross | 7caff80f6b | |
Sean Cross | 6771d28fb4 | |
Sean Cross | 8b54d5addb | |
Sean Cross | 44880465e6 | |
Sean Cross | 49b22a1962 | |
Sean Cross | e66f909bfd | |
Sean Cross | 30d5a097d2 | |
Sean Cross | 21cdcaaee8 | |
Sean Cross | ee006eff90 | |
Sean Cross | 245a466654 | |
Sean Cross | ea7e99af5b | |
Sean Cross | 7f77c49c7b | |
Sean Cross | f9eeee3ff7 | |
Sean Cross | 68694294e1 | |
Sean Cross | 5f0d83b2e2 | |
Sean Cross | d31bcea114 | |
Sean Cross | c447457ffc | |
Sean Cross | 167201228f | |
Sean Cross | 9a14f6d08d | |
Sean Cross | d6403cc3ec | |
Sean Cross | 7647b4a620 | |
Sean Cross | 4b4b993075 | |
Sean Cross | 686640f2e0 | |
Sean Cross | 9295c3d6cc | |
Sean Cross | 249c289aa7 | |
Sean Cross | 397d153a44 | |
Sean Cross | b31c0213b5 | |
Sean Cross | ebed2b919f |
|
@ -2,7 +2,10 @@
|
|||
.swp
|
||||
.swo
|
||||
*~
|
||||
/foboot.bin
|
||||
/foboot.dfu
|
||||
/foboot.ihex
|
||||
/foboot.elf
|
||||
/sw/foboot.bin
|
||||
/sw/foboot.dfu
|
||||
/sw/foboot.ihex
|
||||
/sw/foboot.elf
|
||||
build/
|
||||
__pycache__/
|
||||
.env
|
|
@ -0,0 +1,39 @@
|
|||
[submodule "hw/deps/migen"]
|
||||
path = hw/deps/migen
|
||||
url = https://github.com/m-labs/migen.git
|
||||
[submodule "hw/deps/litex"]
|
||||
path = hw/deps/litex
|
||||
url = https://github.com/enjoy-digital/litex.git
|
||||
[submodule "hw/deps/litescope"]
|
||||
path = hw/deps/litescope
|
||||
url = https://github.com/enjoy-digital/litescope.git
|
||||
[submodule "hw/deps/pyserial"]
|
||||
path = hw/deps/pyserial
|
||||
url = https://github.com/pyserial/pyserial.git
|
||||
[submodule "hw/deps/liteeth"]
|
||||
path = hw/deps/liteeth
|
||||
url = https://github.com/enjoy-digital/liteeth.git
|
||||
[submodule "hw/deps/liteusb"]
|
||||
path = hw/deps/liteusb
|
||||
url = https://github.com/enjoy-digital/liteusb.git
|
||||
[submodule "hw/deps/litedram"]
|
||||
path = hw/deps/litedram
|
||||
url = https://github.com/enjoy-digital/litedram.git
|
||||
[submodule "hw/deps/litepcie"]
|
||||
path = hw/deps/litepcie
|
||||
url = https://github.com/enjoy-digital/litepcie.git
|
||||
[submodule "hw/deps/litesdcard"]
|
||||
path = hw/deps/litesdcard
|
||||
url = https://github.com/enjoy-digital/litesdcard.git
|
||||
[submodule "hw/deps/liteiclink"]
|
||||
path = hw/deps/liteiclink
|
||||
url = https://github.com/enjoy-digital/liteiclink.git
|
||||
[submodule "hw/deps/litevideo"]
|
||||
path = hw/deps/litevideo
|
||||
url = https://github.com/enjoy-digital/litevideo.git
|
||||
[submodule "hw/deps/lxsocsupport"]
|
||||
path = hw/deps/lxsocsupport
|
||||
url = https://github.com/xobs/lxsocsupport.git
|
||||
[submodule "hw/deps/valentyusb"]
|
||||
path = hw/deps/valentyusb
|
||||
url = https://github.com/xobs/valentyusb.git
|
110
Makefile
110
Makefile
|
@ -1,110 +0,0 @@
|
|||
PACKAGE = $(notdir $(realpath .))
|
||||
FOMU_SDK ?= .
|
||||
ADD_CFLAGS = -I$(FOMU_SDK)/include -D__vexriscv__ -march=rv32im -mabi=ilp32
|
||||
ADD_LFLAGS =
|
||||
SRC_DIR = src
|
||||
|
||||
GIT_VERSION= $(shell git describe --tags)
|
||||
TRGT ?= riscv64-unknown-elf-
|
||||
CC = $(TRGT)gcc
|
||||
CXX = $(TRGT)g++
|
||||
OBJCOPY = $(TRGT)objcopy
|
||||
|
||||
RM = rm -rf
|
||||
COPY = cp -a
|
||||
PATH_SEP = /
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
COPY = copy
|
||||
RM = del
|
||||
PATH_SEP = \\
|
||||
endif
|
||||
|
||||
LDSCRIPT = $(FOMU_SDK)/ld/linker.ld
|
||||
LDSCRIPTS = $(LDSCRIPT) $(FOMU_SDK)/ld/output_format.ld $(FOMU_SDK)/ld/regions.ld
|
||||
DBG_CFLAGS = -ggdb -g -DDEBUG -Wall
|
||||
DBG_LFLAGS = -ggdb -g -Wall
|
||||
CFLAGS = $(ADD_CFLAGS) \
|
||||
-Wall -Wextra \
|
||||
-ffunction-sections -fdata-sections -fno-common \
|
||||
-fomit-frame-pointer -Os \
|
||||
-DGIT_VERSION=u\"$(GIT_VERSION)\" -std=gnu11
|
||||
CXXFLAGS = $(CFLAGS) -std=c++11 -fno-rtti -fno-exceptions
|
||||
LFLAGS = $(CFLAGS) $(ADD_LFLAGS) \
|
||||
-nostartfiles \
|
||||
-nostdlib \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,--no-warn-mismatch \
|
||||
-Wl,--script=$(LDSCRIPT) \
|
||||
-Wl,--build-id=none
|
||||
|
||||
OBJ_DIR = .obj
|
||||
|
||||
CSOURCES = $(wildcard $(SRC_DIR)/*.c) $(wildcard third_party/libbase/*.c) $(wildcard third_party/*.c)
|
||||
CPPSOURCES = $(wildcard $(SRC_DIR)/*.cpp) $(wildcard third_party/libbase/*.cpp) $(wildcard third_party/*.cpp)
|
||||
ASOURCES = $(wildcard $(SRC_DIR)/*.S) $(wildcard third_party/libbase/*.S) $(wildcard third_party/*.S)
|
||||
COBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(CSOURCES:.c=.o)))
|
||||
CXXOBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(CPPSOURCES:.cpp=.o)))
|
||||
AOBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(ASOURCES:.S=.o)))
|
||||
OBJECTS = $(COBJS) $(CXXOBJS) $(AOBJS)
|
||||
VPATH = $(SRC_DIR) third_party/libbase third_party
|
||||
|
||||
QUIET = @
|
||||
|
||||
ALL = all
|
||||
TARGET = $(PACKAGE).elf
|
||||
CLEAN = clean
|
||||
|
||||
$(ALL): $(TARGET) $(PACKAGE).bin $(PACKAGE).ihex $(PACKAGE).dfu
|
||||
|
||||
$(OBJECTS): | $(OBJ_DIR)
|
||||
|
||||
$(TARGET): $(OBJECTS) $(LDSCRIPTS)
|
||||
$(QUIET) echo " LD $@"
|
||||
$(QUIET) $(CXX) $(OBJECTS) $(LFLAGS) -o $@
|
||||
|
||||
$(PACKAGE).bin: $(TARGET)
|
||||
$(QUIET) echo " OBJCOPY $@"
|
||||
$(QUIET) $(OBJCOPY) -O binary $(TARGET) $@
|
||||
|
||||
$(PACKAGE).dfu: $(TARGET)
|
||||
$(QUIET) echo " DFU $@"
|
||||
$(QUIET) $(COPY) $(PACKAGE).bin $@
|
||||
$(QUIET) dfu-suffix -v 1209 -p 70b1 -a $@
|
||||
|
||||
$(PACKAGE).ihex: $(TARGET)
|
||||
$(QUIET) echo " IHEX $(PACKAGE).ihex"
|
||||
$(QUIET) $(OBJCOPY) -O ihex $(TARGET) $@
|
||||
|
||||
$(DEBUG): CFLAGS += $(DBG_CFLAGS)
|
||||
$(DEBUG): LFLAGS += $(DBG_LFLAGS)
|
||||
CFLAGS += $(DBG_CFLAGS)
|
||||
LFLAGS += $(DBG_LFLAGS)
|
||||
$(DEBUG): $(TARGET)
|
||||
|
||||
$(OBJ_DIR):
|
||||
$(QUIET) mkdir $(OBJ_DIR)
|
||||
|
||||
$(COBJS) : $(OBJ_DIR)/%.o : %.c Makefile
|
||||
$(QUIET) echo " CC $< $(notdir $@)"
|
||||
$(QUIET) $(CC) -c $< $(CFLAGS) -o $@ -MMD
|
||||
|
||||
$(OBJ_DIR)/%.o: %.cpp
|
||||
$(QUIET) echo " CXX $< $(notdir $@)"
|
||||
$(QUIET) $(CXX) -c $< $(CXXFLAGS) -o $@ -MMD
|
||||
|
||||
$(OBJ_DIR)/%.o: %.S
|
||||
$(QUIET) echo " AS $< $(notdir $@)"
|
||||
$(QUIET) $(CC) -x assembler-with-cpp -c $< $(CFLAGS) -o $@ -MMD
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(QUIET) echo " RM $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))"
|
||||
-$(QUIET) $(RM) $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))
|
||||
$(QUIET) echo " RM $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))"
|
||||
-$(QUIET) $(RM) $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.o))
|
||||
$(QUIET) echo " RM $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol $(PACKAGE).ihex $(PACKAGE).dfu"
|
||||
-$(QUIET) $(RM) $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol $(PACKAGE).ihex $(PACKAGE).dfu
|
||||
|
||||
include $(wildcard $(OBJ_DIR)/*.d)
|
35
README.md
35
README.md
|
@ -0,0 +1,35 @@
|
|||
# Foboot: The Bootloader for Fomu
|
||||
|
||||
Foboot is a failsafe bootloader for Fomu. It exposes a DFU interface to the host. Foboot comes in two halves: A Software half and a Hardware half.
|
||||
|
||||
## Building the Hardware
|
||||
|
||||
The hardware half is written in LiteX, and uses `lxbuildenv` to handle Python dependencies. Hardware is contained within the `hw/` directory. You must install software dependencies yourself:
|
||||
|
||||
* Python 3.5+
|
||||
* Nextpnr
|
||||
* Icestorm
|
||||
* Yosys
|
||||
* Git
|
||||
|
||||
Subproject dependencies will be taken care of with `lxbuildenv`.
|
||||
|
||||
### Usage
|
||||
|
||||
To build, run `foboot-bitstream.py`:
|
||||
|
||||
`python3 .\foboot-bitstream.py`
|
||||
|
||||
There are multiple build options available. Use `--help` to list them.
|
||||
|
||||
Build files will be generated in the `build/` directory. You will probably be most interested in `build/gateware/top.bin` and `build/software/include/generated/`.
|
||||
|
||||
## Tests
|
||||
|
||||
You can run tests by using the `unittest` command:
|
||||
|
||||
`python .\lxbuildenv.py -r -m unittest -v valentyusb.usbcore.cpu.epfifo_test.TestPerEndpointFifoInterface`
|
||||
|
||||
## Building the Software
|
||||
|
||||
Software is contained in the `sw/` directory.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 4}
|
||||
iBus: !!vexriscv.BusReport
|
||||
flushInstructions: [16399, 19, 19, 19]
|
||||
info: !!vexriscv.CacheReport {bytePerLine: 32, size: 1024}
|
||||
kind: cached
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
iBus: !!vexriscv.BusReport
|
||||
flushInstructions: [16399, 19, 19, 19]
|
||||
info: !!vexriscv.CacheReport {bytePerLine: 32, size: 1024}
|
||||
kind: cached
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 4}
|
||||
iBus: !!vexriscv.BusReport
|
||||
flushInstructions: [16399, 19, 19, 19]
|
||||
info: !!vexriscv.CacheReport {bytePerLine: 32, size: 1024}
|
||||
kind: cached
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 4}
|
||||
iBus: !!vexriscv.BusReport
|
||||
flushInstructions: [16399, 19, 19, 19]
|
||||
info: !!vexriscv.CacheReport {bytePerLine: 32, size: 1024}
|
||||
kind: cached
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 4}
|
||||
iBus: !!vexriscv.BusReport
|
||||
flushInstructions: [16399, 19, 19, 19]
|
||||
info: !!vexriscv.CacheReport {bytePerLine: 32, size: 1024}
|
||||
kind: cached
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 4}
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
from litex.utils.litex_read_verilog import main
|
||||
main()
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
from litex.utils.litex_server import main
|
||||
main()
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
from litex.utils.litex_sim import main
|
||||
main()
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
from litex.boards.targets.simple import main
|
||||
main()
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
from litex.utils.litex_term import main
|
||||
main()
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
from litex.soc.tools.mkmscimg import main
|
||||
main()
|
|
@ -0,0 +1,168 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
const uint8_t crc5Table4[] =
|
||||
{
|
||||
0x00, 0x0E, 0x1C, 0x12, 0x11, 0x1F, 0x0D, 0x03,
|
||||
0x0B, 0x05, 0x17, 0x19, 0x1A, 0x14, 0x06, 0x08};
|
||||
const uint8_t crc5Table0[] =
|
||||
{
|
||||
0x00, 0x16, 0x05, 0x13, 0x0A, 0x1C, 0x0F, 0x19,
|
||||
0x14, 0x02, 0x11, 0x07, 0x1E, 0x08, 0x1B, 0x0D};
|
||||
//---------------
|
||||
int crc5Check(const uint8_t *data)
|
||||
//---------------
|
||||
{
|
||||
uint8_t b = data[0] ^ 0x1F;
|
||||
uint8_t crc = crc5Table4[b & 0x0F] ^ crc5Table0[(b >> 4) & 0x0F];
|
||||
b = data[1] ^ crc;
|
||||
return (crc5Table4[b & 0x0F] ^ crc5Table0[(b >> 4) & 0x0F]) == 0x06;
|
||||
}
|
||||
// crc5Check
|
||||
|
||||
int do_check(uint16_t pkt) {
|
||||
uint8_t data[2] = {
|
||||
pkt >> 8,
|
||||
pkt,
|
||||
};
|
||||
return crc5Check(data);
|
||||
}
|
||||
|
||||
#define INT_SIZE 32
|
||||
unsigned CRC5(unsigned dwInput, int iBitcnt)
|
||||
{
|
||||
const uint32_t poly5 = (0x05 << (INT_SIZE-5));
|
||||
uint32_t crc5 = (0x1f << (INT_SIZE-5));
|
||||
uint32_t udata = (dwInput << (INT_SIZE-iBitcnt));
|
||||
|
||||
if ( (iBitcnt<1) || (iBitcnt>INT_SIZE) ) // Validate iBitcnt
|
||||
return 0xffffffff;
|
||||
|
||||
while (iBitcnt--)
|
||||
{
|
||||
if ( (udata ^ crc5) & (0x1<<(INT_SIZE-1)) ) // bit4 != bit4?
|
||||
{
|
||||
crc5 <<= 1;
|
||||
crc5 ^= poly5;
|
||||
}
|
||||
else
|
||||
crc5 <<= 1;
|
||||
|
||||
udata <<= 1;
|
||||
}
|
||||
|
||||
// Shift back into position
|
||||
crc5 >>= (INT_SIZE-5);
|
||||
|
||||
// Invert contents to generate crc field
|
||||
crc5 ^= 0x1f;
|
||||
|
||||
return crc5;
|
||||
} //CRC5()
|
||||
|
||||
static uint32_t reverse_sof(uint32_t data) {
|
||||
int i;
|
||||
uint32_t data_flipped = 0;
|
||||
for (i = 0; i < 11; i++)
|
||||
if (data & (1 << i))
|
||||
data_flipped |= 1 << (10 - i);
|
||||
|
||||
return data_flipped;
|
||||
}
|
||||
|
||||
static uint8_t reverse_byte(uint8_t data) {
|
||||
int i;
|
||||
uint8_t data_flipped = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
if (data & (1 << i))
|
||||
data_flipped |= 1 << (7 - i);
|
||||
|
||||
return data_flipped;
|
||||
}
|
||||
|
||||
static uint8_t reverse_crc5(uint8_t data) {
|
||||
int i;
|
||||
uint8_t data_flipped = 0;
|
||||
for (i = 0; i < 5; i++)
|
||||
if (data & (1 << i))
|
||||
data_flipped |= 1 << (4 - i);
|
||||
|
||||
return data_flipped;
|
||||
}
|
||||
|
||||
static uint16_t make_token(uint16_t data) {
|
||||
uint16_t val = 0;
|
||||
|
||||
data = reverse_sof(data);
|
||||
val = data << 5;
|
||||
val |= CRC5(data, 11);
|
||||
|
||||
return (reverse_byte(val >> 8) << 8) | reverse_byte(val);
|
||||
}
|
||||
|
||||
int do_crc5(uint8_t bfr[2]) {
|
||||
uint8_t pkt_flipped[2] = {
|
||||
reverse_byte(bfr[0]),
|
||||
reverse_byte(bfr[1]),
|
||||
};
|
||||
uint32_t data = (pkt_flipped[1] >> 5) | (pkt_flipped[0] << 3);
|
||||
uint32_t data_flipped;
|
||||
uint8_t crc;
|
||||
uint16_t pkt;
|
||||
((uint8_t *)&pkt)[0] = bfr[1];
|
||||
((uint8_t *)&pkt)[1] = bfr[0];
|
||||
uint8_t found_crc = (pkt >> 3) & 0x1f;
|
||||
|
||||
data_flipped = reverse_sof(data);
|
||||
|
||||
crc = CRC5(data, 11);
|
||||
crc = reverse_crc5(crc);
|
||||
|
||||
uint16_t reconstructed = make_token(data_flipped);
|
||||
uint16_t wire = (reverse_byte(pkt >> 8) << 8) | reverse_byte(pkt);
|
||||
|
||||
printf("Packet: 0x%04x FCRC: %02x Data: 0x%04x "
|
||||
"Flipped: 0x%04x CRC5: 0x%02x Pass? %d Reconstructed: 0x%04x Wire: %04x\n",
|
||||
pkt, found_crc, data, data_flipped, crc, do_check(pkt),
|
||||
reconstructed,
|
||||
wire
|
||||
);
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// uint32_t check_bytes[] = {
|
||||
// /*
|
||||
// 0xff3c,
|
||||
// 0x12c5,
|
||||
// 0xe17e,
|
||||
// 0x19f5,
|
||||
// 0x0225,
|
||||
// 0x0165,
|
||||
// 0x009d,
|
||||
// 0x102f,
|
||||
// make_token(1013),
|
||||
// make_token(1429),
|
||||
// make_token(100),
|
||||
// */
|
||||
// 0x82bc,
|
||||
// make_token(0x0483),//0x5fde,
|
||||
// 0x843c,
|
||||
|
||||
// };
|
||||
uint8_t check_bytes[][2] = {
|
||||
{0x82, 0xbc},
|
||||
{0x83, 0x44},
|
||||
{0x84, 0x3c},
|
||||
};
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(check_bytes); i++)
|
||||
do_crc5(check_bytes[i]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python3
|
||||
# This variable defines all the external programs that this module
|
||||
# relies on. lxbuildenv reads this variable in order to ensure
|
||||
# the build will finish without exiting due to missing third-party
|
||||
# programs.
|
||||
LX_DEPENDENCIES = ["riscv", "icestorm", "yosys"]
|
||||
|
||||
# Import lxbuildenv to integrate the deps/ directory
|
||||
import lxbuildenv
|
||||
|
||||
# Disable pylint's E1101, which breaks completely on migen
|
||||
#pylint:disable=E1101
|
||||
|
||||
from migen import Module, Signal, Instance, ClockDomain, If, run_simulation
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from litex.build.lattice.platform import LatticePlatform
|
||||
from litex.build.generic_platform import Pins, IOStandard, Misc, Subsignal
|
||||
from litex.soc.integration import SoCCore
|
||||
from litex.soc.integration.builder import Builder
|
||||
from litex.soc.integration.soc_core import csr_map_update
|
||||
from litex.soc.interconnect import wishbone
|
||||
from litex.soc.interconnect.csr import CSR, CSRStorage, AutoCSR
|
||||
|
||||
def csr_test(dut):
|
||||
for i in range(20):
|
||||
counter = yield dut.counter
|
||||
# test_value = yield dut.test_value.w
|
||||
(test_value) = yield from dut.test_value.read()
|
||||
print("CSR value: {} / {}".format(counter, test_value))
|
||||
# yield from CSRStorage.update_csrs()
|
||||
yield
|
||||
|
||||
class TestCSR(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
# self.counter = Signal(8)
|
||||
# self.test_value = CSR(8)
|
||||
# self.result = Signal(8)
|
||||
# self.read_result = Signal()
|
||||
# self.sync += [
|
||||
# self.counter.eq(self.counter+1),
|
||||
# self.test_value.w.eq(self.counter),
|
||||
# self.result.eq(self.test_value.r),
|
||||
# self.read_result.eq(self.test_value.re),
|
||||
# ]
|
||||
self.counter = Signal(8)
|
||||
self.test_value = CSRStorage(8, write_from_dev=True)
|
||||
self.result = Signal(8)
|
||||
self.result_re = Signal()
|
||||
self.sync += [
|
||||
self.counter.eq(self.counter+1),
|
||||
self.test_value.we.eq(1),
|
||||
self.test_value.dat_w.eq(self.counter),
|
||||
self.result.eq(self.test_value.storage),
|
||||
self.result_re.eq(self.test_value.re),
|
||||
]
|
||||
|
||||
def main():
|
||||
dut = TestCSR()
|
||||
for x in dir(dut):
|
||||
print("x: {}".format(x))
|
||||
for csr in dut.get_csrs():
|
||||
print("csr: {}".format(csr))
|
||||
if isinstance(csr, CSRStorage) and hasattr(csr, "dat_w"):
|
||||
print("Adding CSRStorage patch")
|
||||
dut.sync += [
|
||||
If(csr.we,
|
||||
csr.storage.eq(csr.dat_w),
|
||||
csr.re.eq(1),
|
||||
).Else(
|
||||
csr.re.eq(0),
|
||||
)
|
||||
]
|
||||
run_simulation(dut, csr_test(dut), vcd_name="csr-test.vcd")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1 @@
|
|||
Subproject commit fd3e9afbcd0872b645badc9b75aacab6dbf7459e
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 77fa4bfb1e452adb1fa34c1b0baede68c056763d
|
|
@ -0,0 +1 @@
|
|||
Subproject commit ee9ae87641d98a1b9ad88e28042394797bfb9e00
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3804c4947adedc6c720e3041e518627b0bf57f78
|
|
@ -0,0 +1 @@
|
|||
Subproject commit c1d8bdf6f23b1070b8bd2dd277a4708863474148
|
|
@ -0,0 +1 @@
|
|||
Subproject commit c07165ff9f9e368c8147158b88fe90cea683e51c
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0a9110f901182a1233cc4e64b6e39175f6784621
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 98e145fba8c25394e9958bad67e2a457d145127e
|
|
@ -0,0 +1 @@
|
|||
Subproject commit fd7ed6c1ec24ffbdbebb465bc8cc4713b6d40181
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 74f133340144630e000f91e466ac5107f87a8d5a
|
|
@ -0,0 +1 @@
|
|||
Subproject commit ae421054f1e3621621bcc397df171caf3681763c
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a4d8f27bf636ee598c4368d7e488f78a226bf778
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 65292f0adbe688bda27f0b202cba9bea28de0d14
|
|
@ -0,0 +1,744 @@
|
|||
#!/usr/bin/env python3
|
||||
# This variable defines all the external programs that this module
|
||||
# relies on. lxbuildenv reads this variable in order to ensure
|
||||
# the build will finish without exiting due to missing third-party
|
||||
# programs.
|
||||
LX_DEPENDENCIES = ["riscv", "icestorm", "yosys"]
|
||||
|
||||
# Import lxbuildenv to integrate the deps/ directory
|
||||
import lxbuildenv
|
||||
|
||||
# Disable pylint's E1101, which breaks completely on migen
|
||||
#pylint:disable=E1101
|
||||
|
||||
#from migen import *
|
||||
from migen import Module, Signal, Instance, ClockDomain, If
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from migen.fhdl.specials import TSTriple
|
||||
from migen.fhdl.structure import ClockSignal, ResetSignal, Replicate, Cat
|
||||
|
||||
from litex.build.lattice.platform import LatticePlatform
|
||||
from litex.build.generic_platform import Pins, IOStandard, Misc, Subsignal
|
||||
from litex.soc.integration import SoCCore
|
||||
from litex.soc.integration.builder import Builder
|
||||
from litex.soc.integration.soc_core import csr_map_update
|
||||
from litex.soc.interconnect import wishbone
|
||||
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
|
||||
|
||||
from valentyusb import usbcore
|
||||
from valentyusb.usbcore import io as usbio
|
||||
from valentyusb.usbcore.cpu import epmem, unififo, epfifo
|
||||
from valentyusb.usbcore.endpoint import EndpointType
|
||||
|
||||
from lxsocsupport import up5kspram, spi_flash
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
_io_evt = [
|
||||
("serial", 0,
|
||||
Subsignal("rx", Pins("21")),
|
||||
Subsignal("tx", Pins("13"), Misc("PULLUP")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
("usb", 0,
|
||||
Subsignal("d_p", Pins("34")),
|
||||
Subsignal("d_n", Pins("37")),
|
||||
Subsignal("pullup", Pins("35")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
("pmoda", 0,
|
||||
Subsignal("p1", Pins("28"), IOStandard("LVCMOS33")),
|
||||
Subsignal("p2", Pins("27"), IOStandard("LVCMOS33")),
|
||||
Subsignal("p3", Pins("26"), IOStandard("LVCMOS33")),
|
||||
Subsignal("p4", Pins("23"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("pmodb", 0,
|
||||
Subsignal("p1", Pins("48"), IOStandard("LVCMOS33")),
|
||||
Subsignal("p2", Pins("47"), IOStandard("LVCMOS33")),
|
||||
Subsignal("p3", Pins("46"), IOStandard("LVCMOS33")),
|
||||
Subsignal("p4", Pins("45"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("led", 0,
|
||||
Subsignal("rgb0", Pins("39"), IOStandard("LVCMOS33")),
|
||||
Subsignal("rgb1", Pins("40"), IOStandard("LVCMOS33")),
|
||||
Subsignal("rgb2", Pins("41"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("spiflash", 0,
|
||||
Subsignal("cs_n", Pins("16"), IOStandard("LVCMOS33")),
|
||||
Subsignal("clk", Pins("15"), IOStandard("LVCMOS33")),
|
||||
Subsignal("miso", Pins("17"), IOStandard("LVCMOS33")),
|
||||
Subsignal("mosi", Pins("14"), IOStandard("LVCMOS33")),
|
||||
Subsignal("wp", Pins("18"), IOStandard("LVCMOS33")),
|
||||
Subsignal("hold", Pins("19"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("spiflash4x", 0,
|
||||
Subsignal("cs_n", Pins("16"), IOStandard("LVCMOS33")),
|
||||
Subsignal("clk", Pins("15"), IOStandard("LVCMOS33")),
|
||||
Subsignal("dq", Pins("14 17 19 18"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("clk48", 0, Pins("44"), IOStandard("LVCMOS33"))
|
||||
]
|
||||
_io_dvt = [
|
||||
("serial", 0,
|
||||
Subsignal("rx", Pins("C3")),
|
||||
Subsignal("tx", Pins("B3"), Misc("PULLUP")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
("usb", 0,
|
||||
Subsignal("d_p", Pins("A1")),
|
||||
Subsignal("d_n", Pins("A2")),
|
||||
Subsignal("pullup", Pins("A4")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
("led", 0,
|
||||
Subsignal("rgb0", Pins("A5"), IOStandard("LVCMOS33")),
|
||||
Subsignal("rgb1", Pins("B5"), IOStandard("LVCMOS33")),
|
||||
Subsignal("rgb2", Pins("C5"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("spiflash", 0,
|
||||
Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("miso", Pins("E1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("mosi", Pins("F1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("wp", Pins("F2"), IOStandard("LVCMOS33")),
|
||||
Subsignal("hold", Pins("B1"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("spiflash4x", 0,
|
||||
Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("dq", Pins("E1 F1 F2 B1"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("clk48", 0, Pins("F4"), IOStandard("LVCMOS33"))
|
||||
]
|
||||
_io_hacker = [
|
||||
("serial", 0,
|
||||
Subsignal("rx", Pins("C3")),
|
||||
Subsignal("tx", Pins("B3"), Misc("PULLUP")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
("usb", 0,
|
||||
Subsignal("d_p", Pins("A4")),
|
||||
Subsignal("d_n", Pins("A2")),
|
||||
Subsignal("pullup", Pins("D5")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
("led", 0,
|
||||
Subsignal("rgb0", Pins("A5"), IOStandard("LVCMOS33")),
|
||||
Subsignal("rgb1", Pins("B5"), IOStandard("LVCMOS33")),
|
||||
Subsignal("rgb2", Pins("C5"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("spiflash", 0,
|
||||
Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("miso", Pins("E1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("mosi", Pins("F1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("wp", Pins("F2"), IOStandard("LVCMOS33")),
|
||||
Subsignal("hold", Pins("B1"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("spiflash4x", 0,
|
||||
Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")),
|
||||
Subsignal("dq", Pins("E1 F1 F2 B1"), IOStandard("LVCMOS33")),
|
||||
),
|
||||
("clk48", 0, Pins("F5"), IOStandard("LVCMOS33"))
|
||||
]
|
||||
|
||||
_connectors = []
|
||||
|
||||
class _CRG(Module):
|
||||
def __init__(self, platform, use_pll):
|
||||
if use_pll:
|
||||
clk48_raw = platform.request("clk48")
|
||||
clk12_raw = Signal()
|
||||
clk48 = Signal()
|
||||
clk12 = Signal()
|
||||
|
||||
# Divide clk48 down to clk12, to ensure they're synchronized.
|
||||
# By doing this, we avoid needing clock-domain crossing.
|
||||
clk12_counter = Signal(2)
|
||||
|
||||
|
||||
self.clock_domains.cd_sys = ClockDomain()
|
||||
self.clock_domains.cd_usb_12 = ClockDomain()
|
||||
self.clock_domains.cd_usb_48 = ClockDomain()
|
||||
self.clock_domains.cd_usb_48_raw = ClockDomain()
|
||||
|
||||
platform.add_period_constraint(self.cd_usb_48.clk, 1e9/48e6)
|
||||
platform.add_period_constraint(self.cd_usb_48_raw.clk, 1e9/48e6)
|
||||
platform.add_period_constraint(self.cd_sys.clk, 1e9/12e6)
|
||||
platform.add_period_constraint(self.cd_usb_12.clk, 1e9/12e6)
|
||||
platform.add_period_constraint(clk48, 1e9/48e6)
|
||||
platform.add_period_constraint(clk48_raw, 1e9/48e6)
|
||||
|
||||
self.reset = Signal()
|
||||
|
||||
# POR reset logic- POR generated from sys clk, POR logic feeds sys clk
|
||||
# reset.
|
||||
self.clock_domains.cd_por = ClockDomain()
|
||||
reset_delay = Signal(14, reset=4095)
|
||||
self.comb += [
|
||||
self.cd_por.clk.eq(self.cd_sys.clk),
|
||||
self.cd_sys.rst.eq(reset_delay != 0),
|
||||
self.cd_usb_12.rst.eq(reset_delay != 0),
|
||||
self.cd_usb_48.rst.eq(reset_delay != 0),
|
||||
# self.cd_usb_48_raw.rst.eq(reset_delay != 0),
|
||||
]
|
||||
|
||||
self.comb += self.cd_usb_48_raw.clk.eq(clk48_raw)
|
||||
self.comb += self.cd_usb_48.clk.eq(clk48)
|
||||
|
||||
self.sync.usb_48_raw += clk12_counter.eq(clk12_counter + 1)
|
||||
|
||||
self.comb += clk12_raw.eq(clk12_counter[1])
|
||||
self.specials += Instance(
|
||||
"SB_GB",
|
||||
i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk12_raw,
|
||||
o_GLOBAL_BUFFER_OUTPUT=clk12,
|
||||
)
|
||||
platform.add_period_constraint(clk12_raw, 1e9/12e6)
|
||||
|
||||
self.specials += Instance(
|
||||
"SB_PLL40_CORE",
|
||||
# Parameters
|
||||
p_DIVR = 0,
|
||||
p_DIVF = 3,
|
||||
p_DIVQ = 2,
|
||||
p_FILTER_RANGE = 1,
|
||||
p_FEEDBACK_PATH = "PHASE_AND_DELAY",
|
||||
p_DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED",
|
||||
p_FDA_FEEDBACK = 15,
|
||||
p_DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED",
|
||||
p_FDA_RELATIVE = 0,
|
||||
p_SHIFTREG_DIV_MODE = 1,
|
||||
p_PLLOUT_SELECT = "SHIFTREG_0deg",
|
||||
p_ENABLE_ICEGATE = 0,
|
||||
# IO
|
||||
i_REFERENCECLK = clk12,
|
||||
# o_PLLOUTCORE = clk12,
|
||||
o_PLLOUTGLOBAL = clk48,
|
||||
#i_EXTFEEDBACK,
|
||||
#i_DYNAMICDELAY,
|
||||
#o_LOCK,
|
||||
i_BYPASS = 0,
|
||||
i_RESETB = 1,
|
||||
#i_LATCHINPUTVALUE,
|
||||
#o_SDO,
|
||||
#i_SDI,
|
||||
)
|
||||
else:
|
||||
clk48_raw = platform.request("clk48")
|
||||
clk12_raw = Signal()
|
||||
clk48 = Signal()
|
||||
clk12 = Signal()
|
||||
|
||||
self.clock_domains.cd_sys = ClockDomain()
|
||||
self.clock_domains.cd_usb_12 = ClockDomain()
|
||||
self.clock_domains.cd_usb_48 = ClockDomain()
|
||||
|
||||
platform.add_period_constraint(self.cd_usb_48.clk, 1e9/48e6)
|
||||
platform.add_period_constraint(self.cd_sys.clk, 1e9/12e6)
|
||||
platform.add_period_constraint(self.cd_usb_12.clk, 1e9/12e6)
|
||||
platform.add_period_constraint(clk48, 1e9/48e6)
|
||||
|
||||
self.reset = Signal()
|
||||
|
||||
# POR reset logic- POR generated from sys clk, POR logic feeds sys clk
|
||||
# reset.
|
||||
self.clock_domains.cd_por = ClockDomain()
|
||||
reset_delay = Signal(13, reset=4095)
|
||||
self.comb += [
|
||||
self.cd_por.clk.eq(self.cd_sys.clk),
|
||||
self.cd_sys.rst.eq(reset_delay != 0),
|
||||
self.cd_usb_12.rst.eq(reset_delay != 0),
|
||||
# self.cd_usb_48.rst.eq(reset_delay != 0),
|
||||
]
|
||||
|
||||
self.specials += Instance(
|
||||
"SB_GB",
|
||||
i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk48_raw,
|
||||
o_GLOBAL_BUFFER_OUTPUT=clk48,
|
||||
)
|
||||
self.comb += self.cd_usb_48.clk.eq(clk48)
|
||||
|
||||
clk12_counter = Signal(2)
|
||||
self.sync.usb_48 += clk12_counter.eq(clk12_counter + 1)
|
||||
|
||||
self.comb += clk12_raw.eq(clk12_counter[1])
|
||||
platform.add_period_constraint(clk12_raw, 1e9/12e6)
|
||||
self.specials += Instance(
|
||||
"SB_GB",
|
||||
i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk12_raw,
|
||||
o_GLOBAL_BUFFER_OUTPUT=clk12,
|
||||
)
|
||||
|
||||
self.comb += self.cd_sys.clk.eq(clk12)
|
||||
self.comb += self.cd_usb_12.clk.eq(clk12)
|
||||
|
||||
self.sync.por += \
|
||||
If(reset_delay != 0,
|
||||
reset_delay.eq(reset_delay - 1)
|
||||
)
|
||||
self.specials += AsyncResetSynchronizer(self.cd_por, self.reset)
|
||||
|
||||
class RandomFirmwareROM(wishbone.SRAM):
|
||||
"""
|
||||
Seed the random data with a fixed number, so different bitstreams
|
||||
can all share firmware.
|
||||
"""
|
||||
def __init__(self, size, seed=2373):
|
||||
def xorshift32(x):
|
||||
x = x ^ (x << 13) & 0xffffffff
|
||||
x = x ^ (x >> 17) & 0xffffffff
|
||||
x = x ^ (x << 5) & 0xffffffff
|
||||
return x & 0xffffffff
|
||||
|
||||
def get_rand(x):
|
||||
out = 0
|
||||
for i in range(32):
|
||||
x = xorshift32(x)
|
||||
if (x & 1) == 1:
|
||||
out = out | (1 << i)
|
||||
return out & 0xffffffff
|
||||
data = []
|
||||
seed = 1
|
||||
for d in range(int(size / 4)):
|
||||
seed = get_rand(seed)
|
||||
data.append(seed)
|
||||
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
|
||||
|
||||
class FirmwareROM(wishbone.SRAM):
|
||||
def __init__(self, size, filename):
|
||||
data = []
|
||||
with open(filename, 'rb') as inp:
|
||||
data = inp.read()
|
||||
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
|
||||
|
||||
class Platform(LatticePlatform):
|
||||
default_clk_name = "clk48"
|
||||
default_clk_period = 20.833
|
||||
|
||||
gateware_size = 0x20000
|
||||
|
||||
def __init__(self, revision=None, toolchain="icestorm"):
|
||||
if revision == "evt":
|
||||
LatticePlatform.__init__(self, "ice40-up5k-sg48", _io_evt, _connectors, toolchain="icestorm")
|
||||
elif revision == "dvt":
|
||||
LatticePlatform.__init__(self, "ice40-up5k-uwg30", _io_dvt, _connectors, toolchain="icestorm")
|
||||
elif revision == "hacker":
|
||||
LatticePlatform.__init__(self, "ice40-up5k-uwg30", _io_hacker, _connectors, toolchain="icestorm")
|
||||
else:
|
||||
raise ValueError("Unrecognized reivsion: {}. Known values: evt, dvt".format(revision))
|
||||
|
||||
def create_programmer(self):
|
||||
raise ValueError("programming is not supported")
|
||||
|
||||
class SBLED(Module, AutoCSR):
|
||||
def __init__(self, pads):
|
||||
rgba_pwm = Signal(3)
|
||||
|
||||
self.dat = CSRStorage(8)
|
||||
self.addr = CSRStorage(4)
|
||||
self.ctrl = CSRStorage(4)
|
||||
|
||||
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],
|
||||
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",
|
||||
)
|
||||
|
||||
self.specials += Instance("SB_LEDDA_IP",
|
||||
i_LEDDCS = self.dat.re,
|
||||
i_LEDDCLK = ClockSignal(),
|
||||
i_LEDDDAT7 = self.dat.storage[7],
|
||||
i_LEDDDAT6 = self.dat.storage[6],
|
||||
i_LEDDDAT5 = self.dat.storage[5],
|
||||
i_LEDDDAT4 = self.dat.storage[4],
|
||||
i_LEDDDAT3 = self.dat.storage[3],
|
||||
i_LEDDDAT2 = self.dat.storage[2],
|
||||
i_LEDDDAT1 = self.dat.storage[1],
|
||||
i_LEDDDAT0 = self.dat.storage[0],
|
||||
i_LEDDADDR3 = self.addr.storage[3],
|
||||
i_LEDDADDR2 = self.addr.storage[2],
|
||||
i_LEDDADDR1 = self.addr.storage[1],
|
||||
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],
|
||||
o_PWMOUT2 = rgba_pwm[2],
|
||||
o_LEDDON = Signal(),
|
||||
)
|
||||
|
||||
class SBWarmBoot(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.ctrl = CSRStorage(size=8)
|
||||
do_reset = Signal()
|
||||
self.comb += [
|
||||
# "Reset Key" is 0xac (0b101011xx)
|
||||
do_reset.eq(self.ctrl.storage[2] & self.ctrl.storage[3] & ~self.ctrl.storage[4]
|
||||
& self.ctrl.storage[5] & ~self.ctrl.storage[6] & self.ctrl.storage[7])
|
||||
]
|
||||
self.specials += Instance("SB_WARMBOOT",
|
||||
i_S0 = self.ctrl.storage[0],
|
||||
i_S1 = self.ctrl.storage[1],
|
||||
i_BOOT = do_reset,
|
||||
)
|
||||
|
||||
|
||||
class BBSpi(Module, AutoCSR):
|
||||
def __init__(self, platform, pads):
|
||||
self.reset = Signal()
|
||||
# self.rdata = Signal(32)
|
||||
# self.addr = Signal(24)
|
||||
# self.ready = Signal()
|
||||
# self.valid = Signal()
|
||||
|
||||
# self.flash_csb = Signal()
|
||||
# self.flash_clk = Signal()
|
||||
|
||||
# cfgreg_we = Signal(4)
|
||||
# cfgreg_di = Signal(32)
|
||||
# cfgreg_do = Signal(32)
|
||||
|
||||
mosi_pad = TSTriple()
|
||||
miso_pad = TSTriple()
|
||||
cs_n_pad = TSTriple()
|
||||
clk_pad = TSTriple()
|
||||
wp_pad = TSTriple()
|
||||
hold_pad = TSTriple()
|
||||
|
||||
self.do = CSRStorage(size=6)
|
||||
self.oe = CSRStorage(size=6)
|
||||
self.di = CSRStatus(size=6)
|
||||
# self.cfg = CSRStorage(size=8)
|
||||
|
||||
# cfg_remapped = Cat(self.cfg.storage[0:7], Signal(7), self.cfg.storage[7])
|
||||
|
||||
# self.comb += self.reset.eq(0)
|
||||
# self.comb += [
|
||||
# cfgreg_di.eq(Cat(self.do.storage, Replicate(2, 0), # Attach "DO" to lower 6 bits
|
||||
# self.oe.storage, Replicate(4, 0), # Attach "OE" to bits 8-11
|
||||
# cfg_remapped)),
|
||||
# cfgreg_we.eq(Cat(self.do.re, self.oe.re, self.cfg.re, self.cfg.re)),
|
||||
# self.di.status.eq(cfgreg_do),
|
||||
# clk_pad.oe.eq(~self.reset),
|
||||
# cs_n_pad.oe.eq(~self.reset),
|
||||
# ]
|
||||
|
||||
self.specials += mosi_pad.get_tristate(pads.mosi)
|
||||
self.specials += miso_pad.get_tristate(pads.miso)
|
||||
self.specials += cs_n_pad.get_tristate(pads.cs_n)
|
||||
self.specials += clk_pad.get_tristate(pads.clk)
|
||||
self.specials += wp_pad.get_tristate(pads.wp)
|
||||
self.specials += hold_pad.get_tristate(pads.hold)
|
||||
|
||||
self.comb += [
|
||||
mosi_pad.oe.eq(self.oe.storage[0]),
|
||||
miso_pad.oe.eq(self.oe.storage[1]),
|
||||
wp_pad.oe.eq(self.oe.storage[2]),
|
||||
hold_pad.oe.eq(self.oe.storage[3]),
|
||||
clk_pad.oe.eq(self.oe.storage[4]),
|
||||
cs_n_pad.oe.eq(self.oe.storage[5]),
|
||||
|
||||
mosi_pad.o.eq(self.do.storage[0]),
|
||||
miso_pad.o.eq(self.do.storage[1]),
|
||||
wp_pad.o.eq(self.do.storage[2]),
|
||||
hold_pad.o.eq(self.do.storage[3]),
|
||||
clk_pad.o.eq(self.do.storage[4]),
|
||||
cs_n_pad.o.eq(self.do.storage[5]),
|
||||
|
||||
self.di.status.eq(Cat(mosi_pad.i, miso_pad.i, wp_pad.i, hold_pad.i, clk_pad.i, cs_n_pad.i)),
|
||||
]
|
||||
# self.specials += Instance("spimemio",
|
||||
# o_flash_io0_oe = mosi_pad.oe,
|
||||
# o_flash_io1_oe = miso_pad.oe,
|
||||
# o_flash_io2_oe = wp_pad.oe,
|
||||
# o_flash_io3_oe = hold_pad.oe,
|
||||
|
||||
# o_flash_io0_do = mosi_pad.o,
|
||||
# o_flash_io1_do = miso_pad.o,
|
||||
# o_flash_io2_do = wp_pad.o,
|
||||
# o_flash_io3_do = hold_pad.o,
|
||||
|
||||
# i_flash_io0_di = mosi_pad.i,
|
||||
# i_flash_io1_di = miso_pad.i,
|
||||
# i_flash_io2_di = wp_pad.i,
|
||||
# i_flash_io3_di = hold_pad.i,
|
||||
|
||||
# i_resetn = ResetSignal() | self.reset,
|
||||
# i_clk = ClockSignal(),
|
||||
|
||||
# i_valid = self.valid,
|
||||
# o_ready = self.ready,
|
||||
# i_addr = self.addr,
|
||||
# o_rdata = self.rdata,
|
||||
|
||||
# i_cfgreg_we = cfgreg_we,
|
||||
# i_cfgreg_di = cfgreg_di,
|
||||
# o_cfgreg_do = cfgreg_do,
|
||||
|
||||
# o_flash_csb = self.flash_csb,
|
||||
# o_flash_clk = self.flash_clk,
|
||||
# )
|
||||
# platform.add_source("spimemio.v")
|
||||
|
||||
class BaseSoC(SoCCore):
|
||||
csr_peripherals = [
|
||||
"cpu_or_bridge",
|
||||
"usb",
|
||||
"bbspi",
|
||||
"reboot",
|
||||
"rgb",
|
||||
]
|
||||
csr_map_update(SoCCore.csr_map, csr_peripherals)
|
||||
|
||||
mem_map = {
|
||||
"spiflash": 0x20000000, # (default shadow @0xa0000000)
|
||||
}
|
||||
mem_map.update(SoCCore.mem_map)
|
||||
|
||||
interrupt_map = {
|
||||
"usb": 3,
|
||||
}
|
||||
interrupt_map.update(SoCCore.interrupt_map)
|
||||
|
||||
def __init__(self, platform, boot_source="rand", debug=False, bios_file=None, use_pll=True, **kwargs):
|
||||
# Disable integrated RAM as we'll add it later
|
||||
self.integrated_sram_size = 0
|
||||
|
||||
clk_freq = int(12e6)
|
||||
self.submodules.crg = _CRG(platform, use_pll=use_pll)
|
||||
|
||||
SoCCore.__init__(self, platform, clk_freq, integrated_sram_size=0, with_uart=False, **kwargs)
|
||||
|
||||
if debug:
|
||||
self.cpu.use_external_variant("2-stage-1024-cache-debug.v")
|
||||
from litex.soc.cores.uart import UARTWishboneBridge
|
||||
self.register_mem("vexriscv_debug", 0xf00f0000, self.cpu.debug_bus, 0x10)
|
||||
self.submodules.uart_bridge = UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=115200)
|
||||
self.add_wb_master(self.uart_bridge.wishbone)
|
||||
else:
|
||||
self.cpu.use_external_variant("2-stage-1024-cache.v")
|
||||
|
||||
# SPRAM- UP5K has single port RAM, might as well use it as SRAM to
|
||||
# free up scarce block RAM.
|
||||
spram_size = 128*1024
|
||||
self.submodules.spram = up5kspram.Up5kSPRAM(size=spram_size)
|
||||
self.register_mem("sram", 0x10000000, self.spram.bus, spram_size)
|
||||
|
||||
if boot_source == "rand":
|
||||
kwargs['cpu_reset_address']=0
|
||||
bios_size = 0x2000
|
||||
self.submodules.random_rom = RandomFirmwareROM(bios_size)
|
||||
self.add_constant("ROM_DISABLE", 1)
|
||||
self.register_rom(self.random_rom.bus, bios_size)
|
||||
elif boot_source == "bios":
|
||||
kwargs['cpu_reset_address']=0
|
||||
if bios_file is None:
|
||||
self.integrated_rom_size = bios_size = 0x2000
|
||||
self.submodules.rom = wishbone.SRAM(bios_size, read_only=True, init=[])
|
||||
self.register_rom(self.rom.bus, bios_size)
|
||||
else:
|
||||
bios_size = 0x2000
|
||||
self.submodules.firmware_rom = FirmwareROM(bios_size, bios_file)
|
||||
self.add_constant("ROM_DISABLE", 1)
|
||||
self.register_rom(self.firmware_rom.bus, bios_size)
|
||||
|
||||
elif boot_source == "spi":
|
||||
bios_size = 0x8000
|
||||
kwargs['cpu_reset_address']=self.mem_map["spiflash"]+platform.gateware_size
|
||||
self.add_memory_region("rom", kwargs['cpu_reset_address'], bios_size)
|
||||
self.add_constant("ROM_DISABLE", 1)
|
||||
self.flash_boot_address = self.mem_map["spiflash"]+platform.gateware_size+bios_size
|
||||
self.add_memory_region("user_flash",
|
||||
self.flash_boot_address,
|
||||
# Leave a grace area- possible one-by-off bug in add_memory_region?
|
||||
# Possible fix: addr < origin + length - 1
|
||||
platform.spiflash_total_size - (self.flash_boot_address - self.mem_map["spiflash"]) - 0x100)
|
||||
else:
|
||||
raise ValueError("unrecognized boot_source: {}".format(boot_source))
|
||||
|
||||
# Add a simple bit-banged SPI Flash module
|
||||
spi_pads = platform.request("spiflash")
|
||||
self.submodules.bbspi = BBSpi(platform, spi_pads)
|
||||
|
||||
self.submodules.reboot = SBWarmBoot()
|
||||
|
||||
self.submodules.rgb = SBLED(platform.request("led"))
|
||||
|
||||
# Add USB pads
|
||||
usb_pads = platform.request("usb")
|
||||
usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
|
||||
self.submodules.usb = epfifo.PerEndpointFifoInterface(usb_iobuf, endpoints=[EndpointType.BIDIR])
|
||||
# self.submodules.usb = epmem.MemInterface(usb_iobuf)
|
||||
# self.submodules.usb = unififo.UsbUniFifo(usb_iobuf)
|
||||
|
||||
# Add "-relut -dffe_min_ce_use 4" to the synth_ice40 command.
|
||||
# The "-reult" adds an additional LUT pass to pack more stuff in,
|
||||
# and the "-dffe_min_ce_use 4" flag prevents Yosys from generating a
|
||||
# Clock Enable signal for a LUT that has fewer than 4 flip-flops.
|
||||
# This increases density, and lets us use the FPGA more efficiently.
|
||||
platform.toolchain.nextpnr_yosys_template[2] += " -dsp -relut -dffe_min_ce_use 5"
|
||||
|
||||
# Disable final deep-sleep power down so firmware words are loaded
|
||||
# onto softcore's address bus.
|
||||
platform.toolchain.build_template[3] = "icepack -s {build_name}.txt {build_name}.bin"
|
||||
platform.toolchain.nextpnr_build_template[2] = "icepack -s {build_name}.txt {build_name}.bin"
|
||||
|
||||
# # Add a "Multiboot" variant
|
||||
# platform.toolchain.nextpnr_build_template[3] = "icepack -s {build_name}.txt {build_name}-multi.bin"
|
||||
|
||||
def make_multiboot_header(filename, boot_offsets=[160]):
|
||||
"""
|
||||
ICE40 allows you to program the SB_WARMBOOT state machine by adding the following
|
||||
values to the bitstream, before any given image:
|
||||
|
||||
[7e aa 99 7e] Sync Header
|
||||
[92 00 k0] Boot mode (k = 1 for cold boot, 0 for warmboot)
|
||||
[44 03 o1 o2 o3] Boot address
|
||||
[82 00 00] Bank offset
|
||||
[01 08] Reboot
|
||||
[...] Padding (up to 32 bytes)
|
||||
|
||||
Note that in ICE40, the second nybble indicates the number of remaining bytes
|
||||
(with the exception of the sync header).
|
||||
|
||||
The above construct is repeated five times:
|
||||
|
||||
INITIAL_BOOT The image loaded at first boot
|
||||
BOOT_S00 The first image for SB_WARMBOOT
|
||||
BOOT_S01 The second image for SB_WARMBOOT
|
||||
BOOT_S10 The third image for SB_WARMBOOT
|
||||
BOOT_S11 The fourth image for SB_WARMBOOT
|
||||
"""
|
||||
while len(boot_offsets) < 5:
|
||||
boot_offsets.append(boot_offsets[0])
|
||||
|
||||
with open(filename, 'wb') as output:
|
||||
for offset in boot_offsets:
|
||||
# Sync Header
|
||||
output.write(bytes([0x7e, 0xaa, 0x99, 0x7e]))
|
||||
|
||||
# Boot mode
|
||||
output.write(bytes([0x92, 0x00, 0x00]))
|
||||
|
||||
# Boot address
|
||||
output.write(bytes([0x44, 0x03,
|
||||
(offset >> 16) & 0xff,
|
||||
(offset >> 8) & 0xff,
|
||||
(offset >> 0) & 0xff]))
|
||||
|
||||
# Bank offset
|
||||
output.write(bytes([0x82, 0x00, 0x00]))
|
||||
|
||||
# Reboot command
|
||||
output.write(bytes([0x01, 0x08]))
|
||||
|
||||
for x in range(17, 32):
|
||||
output.write(bytes([0]))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Build Fomu Main Gateware")
|
||||
parser.add_argument(
|
||||
"--boot-source", choices=["spi", "rand", "bios"], default="bios",
|
||||
help="where to have the CPU obtain its executable code from"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--revision", choices=["dvt", "evt", "hacker"], required=True,
|
||||
help="build foboot for a particular hardware revision"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bios", help="use specified file as a BIOS, rather than building one"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--with-debug", help="enable debug support", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-pll", help="disable pll (possibly improving timing)", action="store_false"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export-random-rom-file", help="Generate a random ROM file and save it to a file"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
output_dir = 'build'
|
||||
|
||||
if args.export_random_rom_file is not None:
|
||||
size = 0x2000
|
||||
def xorshift32(x):
|
||||
x = x ^ (x << 13) & 0xffffffff
|
||||
x = x ^ (x >> 17) & 0xffffffff
|
||||
x = x ^ (x << 5) & 0xffffffff
|
||||
return x & 0xffffffff
|
||||
|
||||
def get_rand(x):
|
||||
out = 0
|
||||
for i in range(32):
|
||||
x = xorshift32(x)
|
||||
if (x & 1) == 1:
|
||||
out = out | (1 << i)
|
||||
return out & 0xffffffff
|
||||
seed = 1
|
||||
with open(args.export_random_rom_file, "w", newline="\n") as output:
|
||||
for d in range(int(size / 4)):
|
||||
seed = get_rand(seed)
|
||||
print("{:08x}".format(seed), file=output)
|
||||
return 0
|
||||
|
||||
compile_software = False
|
||||
if args.boot_source == "bios" and args.bios is None:
|
||||
compile_software = True
|
||||
|
||||
cpu_variant = "min"
|
||||
debug = False
|
||||
if args.with_debug:
|
||||
cpu_variant = "debug"
|
||||
debug = True
|
||||
|
||||
os.environ["LITEX"] = "1" # Give our Makefile something to look for
|
||||
platform = Platform(revision=args.revision)
|
||||
soc = BaseSoC(platform, cpu_type="vexriscv", cpu_variant=cpu_variant,
|
||||
debug=debug, boot_source=args.boot_source,
|
||||
bios_file=args.bios, use_pll=args.no_pll)
|
||||
builder = Builder(soc, output_dir=output_dir, csr_csv="test/csr.csv", compile_software=compile_software)
|
||||
if compile_software:
|
||||
builder.software_packages = [
|
||||
("bios", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "sw")))
|
||||
]
|
||||
vns = builder.build()
|
||||
soc.do_exit(vns)
|
||||
|
||||
make_multiboot_header(os.path.join(output_dir, "gateware", "multiboot-header.bin"), [160, 262144])
|
||||
|
||||
with open(os.path.join(output_dir, 'gateware', 'multiboot-header.bin'), 'rb') as multiboot_header_file:
|
||||
multiboot_header = multiboot_header_file.read()
|
||||
with open(os.path.join(output_dir, 'gateware', 'top.bin'), 'rb') as top_file:
|
||||
top = top_file.read()
|
||||
with open(os.path.join(output_dir, 'gateware', 'top-multiboot.bin'), 'wb') as top_multiboot_file:
|
||||
top_multiboot_file.write(multiboot_header)
|
||||
top_multiboot_file.write(top)
|
||||
|
||||
print(
|
||||
"""Foboot build complete. Output files:
|
||||
{}/gateware/top.bin Bitstream file. Load this onto the FPGA for testing.
|
||||
{}/gateware/top-multiboot.bin Multiboot-enabled bitstream file. Flash this onto FPGA ROM.
|
||||
{}/gateware/top.v Source Verilog file. Useful for debugging issues.
|
||||
{}/software/include/generated Header files for API access.
|
||||
{}/software/bios/bios.elf ELF file for debugging bios.
|
||||
""".format(output_dir, output_dir, output_dir, output_dir, output_dir))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,549 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# This script enables easy, cross-platform building without the need
|
||||
# to install third-party Python modules.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import argparse
|
||||
|
||||
|
||||
DEPS_DIR = "deps"
|
||||
|
||||
DEFAULT_DEPS = {
|
||||
'migen': 'https://github.com/m-labs/migen.git',
|
||||
'litex': 'https://github.com/enjoy-digital/litex.git',
|
||||
'litescope': 'https://github.com/enjoy-digital/litescope.git',
|
||||
'pyserial': 'https://github.com/pyserial/pyserial.git',
|
||||
'liteeth': 'https://github.com/enjoy-digital/liteeth.git',
|
||||
'liteusb': 'https://github.com/enjoy-digital/liteusb.git',
|
||||
'litedram': 'https://github.com/enjoy-digital/litedram.git',
|
||||
'litepcie': 'https://github.com/enjoy-digital/litepcie.git',
|
||||
'litesdcard': 'https://github.com/enjoy-digital/litesdcard.git',
|
||||
'liteiclink': 'https://github.com/enjoy-digital/liteiclink.git',
|
||||
'litevideo': 'https://github.com/enjoy-digital/litevideo.git',
|
||||
}
|
||||
|
||||
# Obtain the path to this script, plus a trailing separator. This will
|
||||
# be used later on to construct various environment variables for paths
|
||||
# to a variety of support directories.
|
||||
script_path = os.path.dirname(os.path.realpath(__file__)) + os.path.sep
|
||||
|
||||
# Look through the specified file for known variables to get the dependency list
|
||||
def get_required_dependencies(filename):
|
||||
import ast
|
||||
|
||||
# Always check the Python version
|
||||
dependencies = {
|
||||
'python': 1
|
||||
}
|
||||
main_src = ""
|
||||
|
||||
try:
|
||||
with open(sys.argv[0], 'r') as f:
|
||||
main_src = f.read()
|
||||
main_ast = ast.parse(main_src, filename=filename)
|
||||
except:
|
||||
return list(dependencies.keys())
|
||||
|
||||
# Iterate through the top-level nodes looking for variables named
|
||||
# LX_DEPENDENCIES or LX_DEPENDENCY and get the values that are
|
||||
# assigned to them.
|
||||
for node in ast.iter_child_nodes(main_ast):
|
||||
if isinstance(node, ast.Assign):
|
||||
value = node.value
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
if target.id == "LX_DEPENDENCIES" or target.id == "LX_DEPENDENCY":
|
||||
if isinstance(value, (ast.List, ast.Tuple)):
|
||||
for elt in value.elts:
|
||||
if isinstance(elt, ast.Str):
|
||||
dependencies[elt.s] = 1
|
||||
elif isinstance(value, ast.Str):
|
||||
dependencies[value.s] = 1
|
||||
|
||||
# Set up sub-dependencies
|
||||
if 'riscv' in dependencies:
|
||||
dependencies['make'] = 1
|
||||
return list(dependencies.keys())
|
||||
|
||||
def get_python_path(script_path, args):
|
||||
# Python has no concept of a local dependency path, such as the C `-I``
|
||||
# switch, or the nodejs `node_modules` path, or the rust cargo registry.
|
||||
# Instead, it relies on an environment variable to append to the search
|
||||
# path.
|
||||
# Construct this variable by adding each subdirectory under the `deps/`
|
||||
# directory to the PYTHONPATH environment variable.
|
||||
python_path = []
|
||||
if os.path.isdir(script_path + DEPS_DIR):
|
||||
for dep in os.listdir(script_path + DEPS_DIR):
|
||||
dep = script_path + DEPS_DIR + os.path.sep + dep
|
||||
if os.path.isdir(dep):
|
||||
python_path.append(dep)
|
||||
return python_path
|
||||
|
||||
def fixup_env(script_path, args):
|
||||
os.environ["PYTHONPATH"] = os.pathsep.join(get_python_path(script_path, 0))
|
||||
|
||||
# Set the "LXBUILDENV_REEXEC" variable to prevent the script from continuously
|
||||
# reinvoking itself.
|
||||
os.environ["LXBUILDENV_REEXEC"] = "1"
|
||||
|
||||
# Python randomizes the order in which it traverses hashes, and Migen uses
|
||||
# hashes an awful lot when bringing together modules. As such, the order
|
||||
# in which Migen generates its output Verilog will change with every run,
|
||||
# and the addresses for various modules will change.
|
||||
# Make builds deterministic so that the generated Verilog code won't change
|
||||
# across runs.
|
||||
os.environ["PYTHONHASHSEED"] = "1"
|
||||
|
||||
# Some Makefiles are invoked as part of the build process, and those Makefiles
|
||||
# occasionally have calls to Python. Ensure those Makefiles use the same
|
||||
# interpreter that this script is using.
|
||||
os.environ["PYTHON"] = sys.executable
|
||||
|
||||
# Set the environment variable "V" to 1. This causes Makefiles to print
|
||||
# the commands they run, which makes them easier to debug.
|
||||
if "lx_verbose" in args and args.lx_verbose:
|
||||
os.environ["V"] = "1"
|
||||
|
||||
# If the user just wanted to print the environment variables, do that and quit.
|
||||
if args.lx_print_env:
|
||||
print("PYTHONPATH={}".format(os.environ["PYTHONPATH"]))
|
||||
print("PYTHONHASHSEED={}".format(os.environ["PYTHONHASHSEED"]))
|
||||
print("PYTHON={}".format(sys.executable))
|
||||
print("LXBUILDENV_REEXEC={}".format(os.environ["LXBUILDENV_REEXEC"]))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Equivalent to the powershell Get-Command, and kinda like `which`
|
||||
def get_command(cmd):
|
||||
if os.name == 'nt':
|
||||
path_ext = os.environ["PATHEXT"].split(os.pathsep)
|
||||
else:
|
||||
path_ext = [""]
|
||||
for ext in path_ext:
|
||||
for path in os.environ["PATH"].split(os.pathsep):
|
||||
|
||||
if os.path.exists(path + os.path.sep + cmd + ext):
|
||||
return path + os.path.sep + cmd + ext
|
||||
return None
|
||||
|
||||
def check_python_version(args):
|
||||
import platform
|
||||
# Litex / Migen require Python 3.5 or newer. Ensure we're running
|
||||
# under a compatible version of Python.
|
||||
if sys.version_info[:3] < (3, 5):
|
||||
return (False,
|
||||
"python: You need Python 3.5+ (version {} found)".format(sys.version_info[:3]))
|
||||
return (True, "python 3.5+: ok (Python {} found)".format(platform.python_version()))
|
||||
|
||||
def check_vivado(args):
|
||||
vivado_path = get_command("vivado")
|
||||
if vivado_path == None:
|
||||
# Look for the default Vivado install directory
|
||||
if os.name == 'nt':
|
||||
base_dir = r"C:\Xilinx\Vivado"
|
||||
else:
|
||||
base_dir = "/opt/Xilinx/Vivado"
|
||||
if os.path.exists(base_dir):
|
||||
for file in os.listdir(base_dir):
|
||||
bin_dir = base_dir + os.path.sep + file + os.path.sep + "bin"
|
||||
if os.path.exists(bin_dir + os.path.sep + "vivado"):
|
||||
os.environ["PATH"] += os.pathsep + bin_dir
|
||||
vivado_path = bin_dir
|
||||
break
|
||||
if vivado_path == None:
|
||||
return (False, "toolchain not found in your PATH", "download it from https://www.xilinx.com/support/download.html")
|
||||
return (True, "found at {}".format(vivado_path))
|
||||
|
||||
def check_cmd(args, cmd, name=None, fix=None):
|
||||
if name is None:
|
||||
name = cmd
|
||||
path = get_command(cmd)
|
||||
if path == None:
|
||||
return (False, name + " not found in your PATH", fix)
|
||||
return (True, "found at {}".format(path))
|
||||
|
||||
def check_make(args):
|
||||
return check_cmd(args, "make", "GNU Make")
|
||||
|
||||
def check_riscv(args):
|
||||
riscv64 = check_cmd(args, "riscv64-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/products/tools/")
|
||||
if riscv64[0] == True:
|
||||
return riscv64
|
||||
|
||||
riscv32 = check_cmd(args, "riscv32-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/products/tools/")
|
||||
if riscv32[0] == True:
|
||||
return riscv32
|
||||
|
||||
return riscv64
|
||||
|
||||
def check_yosys(args):
|
||||
return check_cmd(args, "yosys")
|
||||
|
||||
def check_arachne(args):
|
||||
return check_cmd(args, "arachne-pnr")
|
||||
|
||||
def check_icestorm(args):
|
||||
return check_cmd(args, "icepack") and check_cmd(args, "nextpnr-ice40")
|
||||
|
||||
dependency_checkers = {
|
||||
'python': check_python_version,
|
||||
'vivado': check_vivado,
|
||||
'make': check_make,
|
||||
'riscv': check_riscv,
|
||||
'yosys': check_yosys,
|
||||
'arachne-pnr': check_arachne,
|
||||
'icestorm': check_icestorm,
|
||||
}
|
||||
|
||||
# Validate that the required dependencies (Vivado, compilers, etc.)
|
||||
# have been installed.
|
||||
def check_dependencies(args, dependency_list):
|
||||
|
||||
dependency_errors = 0
|
||||
for dependency_name in dependency_list:
|
||||
if not dependency_name in dependency_checkers:
|
||||
print('WARNING: Unrecognized dependency "{}"'.format(dependency_name))
|
||||
continue
|
||||
result = dependency_checkers[dependency_name](args)
|
||||
if result[0] == False:
|
||||
if len(result) > 2:
|
||||
print('{}: {} -- {}'.format(dependency_name, result[1], result[2]))
|
||||
else:
|
||||
print('{}: {}'.format(dependency_name, result[1]))
|
||||
dependency_errors = dependency_errors + 1
|
||||
|
||||
elif args.lx_check_deps or args.lx_verbose:
|
||||
print('dependency: {}: {}'.format(dependency_name, result[1]))
|
||||
if dependency_errors > 0:
|
||||
if args.lx_ignore_deps:
|
||||
print('{} missing dependencies were found but continuing anyway'.format(dependency_errors))
|
||||
else:
|
||||
raise SystemExit(str(dependency_errors) +
|
||||
" missing dependencies were found")
|
||||
|
||||
if args.lx_check_deps:
|
||||
sys.exit(0)
|
||||
|
||||
# Return True if the given tree needs to be initialized
|
||||
def check_module_recursive(root_path, depth, verbose=False, breadcrumbs=[]):
|
||||
if verbose:
|
||||
print('git-dep: checking if "{}" requires updating (depth: {})...'.format(root_path, depth))
|
||||
|
||||
# If the directory isn't a valid git repo, initialization is required
|
||||
git_dir_cmd = subprocess.Popen(["git", "rev-parse", "--show-toplevel"],
|
||||
cwd=root_path,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(git_stdout, _) = git_dir_cmd.communicate()
|
||||
if git_dir_cmd.wait() != 0:
|
||||
if verbose:
|
||||
print('git-dep: missing git directory, starting update...')
|
||||
return True
|
||||
git_dir = git_stdout.decode().strip()
|
||||
|
||||
if git_dir in breadcrumbs:
|
||||
if verbose:
|
||||
print('git-dep: root path {} is not in git path'.format(root_path))
|
||||
return True
|
||||
breadcrumbs.append(git_dir)
|
||||
|
||||
if not os.path.exists(git_dir + os.path.sep + '.git'):
|
||||
if verbose:
|
||||
print('git-dep: .git not found in "{}"'.format(git_dir))
|
||||
return True
|
||||
|
||||
# If there are no submodules, no initialization needs to be done
|
||||
if not os.path.isfile(git_dir + os.path.sep + '.gitmodules'):
|
||||
if verbose:
|
||||
print('git-dep: .gitmodules not found in "{}", so not updating'.format(git_dir))
|
||||
return False
|
||||
|
||||
# Loop through the gitmodules to check all submodules
|
||||
gitmodules = open(git_dir + os.path.sep + '.gitmodules', 'r')
|
||||
for line in gitmodules:
|
||||
parts = line.split("=", 2)
|
||||
if parts[0].strip() == "path":
|
||||
path = parts[1].strip()
|
||||
if check_module_recursive(git_dir + os.path.sep + path, depth + 1, verbose=verbose, breadcrumbs=breadcrumbs):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Determine whether we need to invoke "git submodules init --recurse"
|
||||
def check_submodules(script_path, args):
|
||||
if check_module_recursive(script_path, 0, verbose=args.lx_verbose):
|
||||
print("Missing submodules -- updating")
|
||||
subprocess.Popen(["git", "submodule", "update",
|
||||
"--init", "--recursive"], cwd=script_path).wait()
|
||||
elif args.lx_verbose:
|
||||
print("Submodule check: Submodules found")
|
||||
|
||||
|
||||
def lx_git(cmd, *args):
|
||||
import subprocess
|
||||
git_cmd = ["git", cmd]
|
||||
if args is not None:
|
||||
for arg in args:
|
||||
git_cmd = git_cmd + [arg]
|
||||
subprocess.call(git_cmd)
|
||||
|
||||
def lx_print_deps():
|
||||
print('Known dependencies:')
|
||||
for dep in dependency_checkers.keys():
|
||||
print(' {}'.format(dep))
|
||||
print('To define a dependency, add a variable inside {} at the top level called LX_DEPENDENCIES and assign it a list or tuple.'.format(sys.argv[0]))
|
||||
print('For example:')
|
||||
print('LX_DEPENDENCIES = ("riscv", "vivado")')
|
||||
|
||||
|
||||
def lx_main(args):
|
||||
if args.lx_print_env:
|
||||
fixup_env(script_path, args)
|
||||
|
||||
elif args.lx_print_deps:
|
||||
lx_print_deps()
|
||||
|
||||
elif args.lx_run is not None:
|
||||
script_name=args.lx_run[0]
|
||||
get_required_dependencies(script_name)
|
||||
|
||||
fixup_env(script_path, args)
|
||||
check_submodules(script_path, args)
|
||||
|
||||
try:
|
||||
sys.exit(subprocess.Popen(
|
||||
[sys.executable] + [script_name] + args.lx_run[1:]).wait())
|
||||
except:
|
||||
sys.exit(1)
|
||||
elif args.init:
|
||||
if args.main is None:
|
||||
main_name = os.getcwd().split(os.path.sep)[-1] + '.py'
|
||||
new_main_name = input('What would you like your main program to be called? [' + main_name + '] ')
|
||||
if new_main_name is not None and new_main_name != "":
|
||||
main_name = new_main_name
|
||||
else:
|
||||
main_name = args.main
|
||||
if not main_name.endswith('.py'):
|
||||
main_name = main_name + '.py'
|
||||
|
||||
if args.no_git:
|
||||
print("skipping git initialization")
|
||||
else:
|
||||
if not os.path.exists(DEPS_DIR):
|
||||
os.mkdir(DEPS_DIR)
|
||||
|
||||
if not os.path.exists(".git"):
|
||||
print("initializing git repository")
|
||||
lx_git('init')
|
||||
else:
|
||||
print("using existing git repository")
|
||||
lx_git('add', str(__file__))
|
||||
|
||||
for dep_name, dep_url in DEFAULT_DEPS.items():
|
||||
dest_path = '{}{}{}'.format(DEPS_DIR, '/', dep_name)
|
||||
if not os.path.exists(dest_path):
|
||||
lx_git('submodule', 'add', dep_url, dest_path)
|
||||
lx_git('add', dest_path)
|
||||
|
||||
lx_git('submodule', 'update', '--init', '--recursive')
|
||||
|
||||
if args.no_bin:
|
||||
print("skipping bin/ initialization")
|
||||
elif os.path.exists("bin"):
|
||||
print("bin/ directory exists -- remove bin/ directory to re-initialize")
|
||||
else:
|
||||
bin_tools = {
|
||||
'mkmscimg': 'litex.soc.tools.mkmscimg',
|
||||
'litex_term': 'litex.utils.litex_term',
|
||||
'litex_server': 'litex.utils.litex_server',
|
||||
'litex_sim': 'litex.utils.litex_sim',
|
||||
'litex_read_verilog': 'litex.utils.litex_read_verilog',
|
||||
'litex_simple': 'litex.boards.targets.simple',
|
||||
}
|
||||
bin_template = """#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# This script lives in the "bin" directory, but uses a helper script in the parent
|
||||
# directory. Obtain the current path so we can get the absolute parent path.
|
||||
script_path = os.path.dirname(os.path.realpath(
|
||||
__file__)) + os.path.sep + os.path.pardir + os.path.sep
|
||||
sys.path.insert(0, script_path)
|
||||
import lxbuildenv
|
||||
|
||||
"""
|
||||
print("Creating binaries")
|
||||
os.mkdir("bin")
|
||||
for bin_name, python_module in bin_tools.items():
|
||||
with open('bin' + os.path.sep + bin_name, 'w') as new_bin:
|
||||
new_bin.write(bin_template)
|
||||
new_bin.write('from ' + python_module + ' import main\n')
|
||||
new_bin.write('main()\n')
|
||||
import stat
|
||||
os.chmod('bin' + os.path.sep + bin_name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
if not args.no_git:
|
||||
lx_git('add', '--chmod=+x', 'bin' + os.path.sep + bin_name)
|
||||
|
||||
if os.path.exists(main_name):
|
||||
print("skipping creation of {}: file exists".format(main_name))
|
||||
else:
|
||||
print("creating main program {}".format(main_name))
|
||||
with open(main_name, 'w') as m:
|
||||
program_template = """#!/usr/bin/env python3
|
||||
# This variable defines all the external programs that this module
|
||||
# relies on. lxbuildenv reads this variable in order to ensure
|
||||
# the build will finish without exiting due to missing third-party
|
||||
# programs.
|
||||
LX_DEPENDENCIES = ["riscv", "vivado"]
|
||||
|
||||
# Import lxbuildenv to integrate the deps/ directory
|
||||
import lxbuildenv
|
||||
|
||||
# Disable pylint's E1101, which breaks completely on migen
|
||||
#pylint:disable=E1101
|
||||
|
||||
from migen import *
|
||||
from litex.build.xilinx import VivadoProgrammer, XilinxPlatform
|
||||
from litex.build.generic_platform import Pins, IOStandard
|
||||
from litex.soc.integration import SoCSDRAM
|
||||
from litex.soc.integration.builder import Builder
|
||||
from litex.soc.integration.soc_core import csr_map_update
|
||||
|
||||
_io = [
|
||||
("clk50", 0, Pins("J19"), IOStandard("LVCMOS33")),
|
||||
]
|
||||
|
||||
class Platform(XilinxPlatform):
|
||||
def __init__(self, toolchain="vivado", programmer="vivado", part="35"):
|
||||
part = "xc7a" + part + "t-fgg484-2"
|
||||
def create_programmer(self):
|
||||
if self.programmer == "vivado":
|
||||
return VivadoProgrammer(flash_part="n25q128-3.3v-spi-x1_x2_x4")
|
||||
else:
|
||||
raise ValueError("{} programmer is not supported"
|
||||
.format(self.programmer))
|
||||
|
||||
def do_finalize(self, fragment):
|
||||
XilinxPlatform.do_finalize(self, fragment)
|
||||
|
||||
class BaseSoC(SoCSDRAM):
|
||||
csr_peripherals = [
|
||||
"ddrphy",
|
||||
# "dna",
|
||||
"xadc",
|
||||
"cpu_or_bridge",
|
||||
]
|
||||
csr_map_update(SoCSDRAM.csr_map, csr_peripherals)
|
||||
|
||||
def __init__(self, platform, **kwargs):
|
||||
clk_freq = int(100e6)
|
||||
|
||||
def main():
|
||||
platform = Platform()
|
||||
soc = BaseSoC(platform)
|
||||
builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv")
|
||||
vns = builder.build()
|
||||
soc.do_exit(vns)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"""
|
||||
m.write(program_template)
|
||||
if not args.no_git:
|
||||
lx_git("add", main_name)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
# For the main command, parse args and hand it off to main()
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Wrap Python code to enable quickstart",
|
||||
add_help=False)
|
||||
parser.add_argument(
|
||||
"-h", "--help", '--lx-help', help="show this help message and exit", action="help"
|
||||
)
|
||||
parser.add_argument(
|
||||
'-i', '--init', '--lx-init', help='initialize a new project', action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
'-m', '--main', '--lx-main', help='name of main project'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-bin', '--lx-no-bin', help="don't create a bin/ directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-git', '--lx-no-git', help="Don't create a git repository"
|
||||
)
|
||||
parser.add_argument(
|
||||
'-e', '--print-env', '--lx-print-env', dest="lx_print_env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--print-deps', '--lx-print-deps', dest="lx_print_deps", help="print all possible dependencies and then exit", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lx-verbose", help="increase verboseness of some processes", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r', '--run', '--lx-run', dest='lx_run', help="run the given script under lxbuildenv", nargs=argparse.REMAINDER
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not lx_main(args):
|
||||
parser.print_help()
|
||||
|
||||
elif not os.path.isfile(sys.argv[0]):
|
||||
print("lxbuildenv doesn't operate while in interactive mode")
|
||||
|
||||
elif "LXBUILDENV_REEXEC" not in os.environ:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Wrap Python code to enable quickstart",
|
||||
add_help=False)
|
||||
parser.add_argument(
|
||||
"--lx-verbose", help="increase verboseness of some processes", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lx-print-env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lx-check-deps", help="check build environment for dependencies such as compiler and fpga tools and then exit", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lx-print-deps", help="print all possible dependencies and then exit", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lx-help", action="help"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lx-ignore-deps", help="try building even if dependencies are missing", action="store_true"
|
||||
)
|
||||
(args, rest) = parser.parse_known_args()
|
||||
|
||||
if args.lx_print_deps:
|
||||
lx_print_deps()
|
||||
sys.exit(0)
|
||||
|
||||
deps = get_required_dependencies(sys.argv[0])
|
||||
|
||||
fixup_env(script_path, args)
|
||||
check_dependencies(args, deps)
|
||||
check_submodules(script_path, args)
|
||||
|
||||
try:
|
||||
sys.exit(subprocess.Popen(
|
||||
[sys.executable] + [sys.argv[0]] + rest).wait())
|
||||
except:
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Overwrite the deps directory.
|
||||
# Because we're running with a predefined PYTHONPATH, you'd think that
|
||||
# the DEPS_DIR would be first.
|
||||
# Unfortunately, setuptools causes the sitewide packages to take precedence
|
||||
# over the PYTHONPATH variable.
|
||||
# Work around this bug by inserting paths into the first index.
|
||||
for path in get_python_path(script_path, None):
|
||||
sys.path.insert(0, path)
|
|
@ -1,347 +0,0 @@
|
|||
#ifndef __GENERATED_CSR_H
|
||||
#define __GENERATED_CSR_H
|
||||
#include <stdint.h>
|
||||
#ifdef CSR_ACCESSORS_DEFINED
|
||||
extern void csr_writeb(uint8_t value, uint32_t addr);
|
||||
extern uint8_t csr_readb(uint32_t addr);
|
||||
extern void csr_writew(uint16_t value, uint32_t addr);
|
||||
extern uint16_t csr_readw(uint32_t addr);
|
||||
extern void csr_writel(uint32_t value, uint32_t addr);
|
||||
extern uint32_t csr_readl(uint32_t addr);
|
||||
#else /* ! CSR_ACCESSORS_DEFINED */
|
||||
#include <hw/common.h>
|
||||
#endif /* ! CSR_ACCESSORS_DEFINED */
|
||||
|
||||
/* spiflash */
|
||||
#define CSR_SPIFLASH_BASE 0xe0004800
|
||||
#define CSR_SPIFLASH_BITBANG_ADDR 0xe0004800
|
||||
#define CSR_SPIFLASH_BITBANG_SIZE 1
|
||||
static inline unsigned char spiflash_bitbang_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004800);
|
||||
return r;
|
||||
}
|
||||
static inline void spiflash_bitbang_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004800);
|
||||
}
|
||||
#define CSR_SPIFLASH_MISO_ADDR 0xe0004804
|
||||
#define CSR_SPIFLASH_MISO_SIZE 1
|
||||
static inline unsigned char spiflash_miso_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004804);
|
||||
return r;
|
||||
}
|
||||
#define CSR_SPIFLASH_BITBANG_EN_ADDR 0xe0004808
|
||||
#define CSR_SPIFLASH_BITBANG_EN_SIZE 1
|
||||
static inline unsigned char spiflash_bitbang_en_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004808);
|
||||
return r;
|
||||
}
|
||||
static inline void spiflash_bitbang_en_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004808);
|
||||
}
|
||||
|
||||
/* timer0 */
|
||||
#define CSR_TIMER0_BASE 0xe0002800
|
||||
#define CSR_TIMER0_LOAD_ADDR 0xe0002800
|
||||
#define CSR_TIMER0_LOAD_SIZE 4
|
||||
static inline unsigned int timer0_load_read(void) {
|
||||
unsigned int r = csr_readl(0xe0002800);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002804);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002808);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000280c);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_load_write(unsigned int value) {
|
||||
csr_writel(value >> 24, 0xe0002800);
|
||||
csr_writel(value >> 16, 0xe0002804);
|
||||
csr_writel(value >> 8, 0xe0002808);
|
||||
csr_writel(value, 0xe000280c);
|
||||
}
|
||||
#define CSR_TIMER0_RELOAD_ADDR 0xe0002810
|
||||
#define CSR_TIMER0_RELOAD_SIZE 4
|
||||
static inline unsigned int timer0_reload_read(void) {
|
||||
unsigned int r = csr_readl(0xe0002810);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002814);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002818);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000281c);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_reload_write(unsigned int value) {
|
||||
csr_writel(value >> 24, 0xe0002810);
|
||||
csr_writel(value >> 16, 0xe0002814);
|
||||
csr_writel(value >> 8, 0xe0002818);
|
||||
csr_writel(value, 0xe000281c);
|
||||
}
|
||||
#define CSR_TIMER0_EN_ADDR 0xe0002820
|
||||
#define CSR_TIMER0_EN_SIZE 1
|
||||
static inline unsigned char timer0_en_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002820);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_en_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002820);
|
||||
}
|
||||
#define CSR_TIMER0_UPDATE_VALUE_ADDR 0xe0002824
|
||||
#define CSR_TIMER0_UPDATE_VALUE_SIZE 1
|
||||
static inline unsigned char timer0_update_value_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002824);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_update_value_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002824);
|
||||
}
|
||||
#define CSR_TIMER0_VALUE_ADDR 0xe0002828
|
||||
#define CSR_TIMER0_VALUE_SIZE 4
|
||||
static inline unsigned int timer0_value_read(void) {
|
||||
unsigned int r = csr_readl(0xe0002828);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000282c);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002830);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002834);
|
||||
return r;
|
||||
}
|
||||
#define CSR_TIMER0_EV_STATUS_ADDR 0xe0002838
|
||||
#define CSR_TIMER0_EV_STATUS_SIZE 1
|
||||
static inline unsigned char timer0_ev_status_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002838);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_ev_status_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002838);
|
||||
}
|
||||
#define CSR_TIMER0_EV_PENDING_ADDR 0xe000283c
|
||||
#define CSR_TIMER0_EV_PENDING_SIZE 1
|
||||
static inline unsigned char timer0_ev_pending_read(void) {
|
||||
unsigned char r = csr_readl(0xe000283c);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_ev_pending_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000283c);
|
||||
}
|
||||
#define CSR_TIMER0_EV_ENABLE_ADDR 0xe0002840
|
||||
#define CSR_TIMER0_EV_ENABLE_SIZE 1
|
||||
static inline unsigned char timer0_ev_enable_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002840);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_ev_enable_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002840);
|
||||
}
|
||||
|
||||
/* uart */
|
||||
#define CSR_UART_BASE 0xe0001800
|
||||
#define CSR_UART_RXTX_ADDR 0xe0001800
|
||||
#define CSR_UART_RXTX_SIZE 1
|
||||
static inline unsigned char uart_rxtx_read(void) {
|
||||
unsigned char r = csr_readl(0xe0001800);
|
||||
return r;
|
||||
}
|
||||
static inline void uart_rxtx_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0001800);
|
||||
}
|
||||
#define CSR_UART_TXFULL_ADDR 0xe0001804
|
||||
#define CSR_UART_TXFULL_SIZE 1
|
||||
static inline unsigned char uart_txfull_read(void) {
|
||||
unsigned char r = csr_readl(0xe0001804);
|
||||
return r;
|
||||
}
|
||||
#define CSR_UART_RXEMPTY_ADDR 0xe0001808
|
||||
#define CSR_UART_RXEMPTY_SIZE 1
|
||||
static inline unsigned char uart_rxempty_read(void) {
|
||||
unsigned char r = csr_readl(0xe0001808);
|
||||
return r;
|
||||
}
|
||||
#define CSR_UART_EV_STATUS_ADDR 0xe000180c
|
||||
#define CSR_UART_EV_STATUS_SIZE 1
|
||||
static inline unsigned char uart_ev_status_read(void) {
|
||||
unsigned char r = csr_readl(0xe000180c);
|
||||
return r;
|
||||
}
|
||||
static inline void uart_ev_status_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000180c);
|
||||
}
|
||||
#define CSR_UART_EV_PENDING_ADDR 0xe0001810
|
||||
#define CSR_UART_EV_PENDING_SIZE 1
|
||||
static inline unsigned char uart_ev_pending_read(void) {
|
||||
unsigned char r = csr_readl(0xe0001810);
|
||||
return r;
|
||||
}
|
||||
static inline void uart_ev_pending_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0001810);
|
||||
}
|
||||
#define CSR_UART_EV_ENABLE_ADDR 0xe0001814
|
||||
#define CSR_UART_EV_ENABLE_SIZE 1
|
||||
static inline unsigned char uart_ev_enable_read(void) {
|
||||
unsigned char r = csr_readl(0xe0001814);
|
||||
return r;
|
||||
}
|
||||
static inline void uart_ev_enable_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0001814);
|
||||
}
|
||||
|
||||
/* uart_phy */
|
||||
#define CSR_UART_PHY_BASE 0xe0001000
|
||||
#define CSR_UART_PHY_TUNING_WORD_ADDR 0xe0001000
|
||||
#define CSR_UART_PHY_TUNING_WORD_SIZE 4
|
||||
static inline unsigned int uart_phy_tuning_word_read(void) {
|
||||
unsigned int r = csr_readl(0xe0001000);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0001004);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0001008);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000100c);
|
||||
return r;
|
||||
}
|
||||
static inline void uart_phy_tuning_word_write(unsigned int value) {
|
||||
csr_writel(value >> 24, 0xe0001000);
|
||||
csr_writel(value >> 16, 0xe0001004);
|
||||
csr_writel(value >> 8, 0xe0001008);
|
||||
csr_writel(value, 0xe000100c);
|
||||
}
|
||||
|
||||
/* usb */
|
||||
#define CSR_USB_BASE 0xe0005000
|
||||
#define CSR_USB_OBUF_HEAD_ADDR 0xe0005000
|
||||
#define CSR_USB_OBUF_HEAD_SIZE 1
|
||||
static inline unsigned char usb_obuf_head_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005000);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_obuf_head_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005000);
|
||||
}
|
||||
#define CSR_USB_OBUF_EMPTY_ADDR 0xe0005004
|
||||
#define CSR_USB_OBUF_EMPTY_SIZE 1
|
||||
static inline unsigned char usb_obuf_empty_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005004);
|
||||
return r;
|
||||
}
|
||||
#define CSR_USB_ARM_ADDR 0xe0005008
|
||||
#define CSR_USB_ARM_SIZE 1
|
||||
static inline unsigned char usb_arm_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005008);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_arm_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005008);
|
||||
}
|
||||
#define CSR_USB_IBUF_HEAD_ADDR 0xe000500c
|
||||
#define CSR_USB_IBUF_HEAD_SIZE 1
|
||||
static inline unsigned char usb_ibuf_head_read(void) {
|
||||
unsigned char r = csr_readl(0xe000500c);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ibuf_head_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000500c);
|
||||
}
|
||||
#define CSR_USB_IBUF_EMPTY_ADDR 0xe0005010
|
||||
#define CSR_USB_IBUF_EMPTY_SIZE 1
|
||||
static inline unsigned char usb_ibuf_empty_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005010);
|
||||
return r;
|
||||
}
|
||||
#define CSR_USB_PULLUP_OUT_ADDR 0xe0005014
|
||||
#define CSR_USB_PULLUP_OUT_SIZE 1
|
||||
static inline unsigned char usb_pullup_out_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005014);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_pullup_out_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005014);
|
||||
}
|
||||
#define CSR_USB_EV_STATUS_ADDR 0xe0005018
|
||||
#define CSR_USB_EV_STATUS_SIZE 1
|
||||
static inline unsigned char usb_ev_status_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005018);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ev_status_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005018);
|
||||
}
|
||||
#define CSR_USB_EV_PENDING_ADDR 0xe000501c
|
||||
#define CSR_USB_EV_PENDING_SIZE 1
|
||||
static inline unsigned char usb_ev_pending_read(void) {
|
||||
unsigned char r = csr_readl(0xe000501c);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ev_pending_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000501c);
|
||||
}
|
||||
#define CSR_USB_EV_ENABLE_ADDR 0xe0005020
|
||||
#define CSR_USB_EV_ENABLE_SIZE 1
|
||||
static inline unsigned char usb_ev_enable_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005020);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ev_enable_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005020);
|
||||
}
|
||||
#define CSR_IDENTIFIER_MEM_BASE 0xe0002000
|
||||
|
||||
/* constants */
|
||||
#define NMI_INTERRUPT 0
|
||||
static inline int nmi_interrupt_read(void) {
|
||||
return 0;
|
||||
}
|
||||
#define TIMER0_INTERRUPT 1
|
||||
static inline int timer0_interrupt_read(void) {
|
||||
return 1;
|
||||
}
|
||||
#define UART_INTERRUPT 2
|
||||
static inline int uart_interrupt_read(void) {
|
||||
return 2;
|
||||
}
|
||||
#define USB_INTERRUPT 3
|
||||
static inline int usb_interrupt_read(void) {
|
||||
return 3;
|
||||
}
|
||||
#define CSR_DATA_WIDTH 8
|
||||
static inline int csr_data_width_read(void) {
|
||||
return 8;
|
||||
}
|
||||
#define SYSTEM_CLOCK_FREQUENCY 12000000
|
||||
static inline int system_clock_frequency_read(void) {
|
||||
return 12000000;
|
||||
}
|
||||
#define SPIFLASH_PAGE_SIZE 256
|
||||
static inline int spiflash_page_size_read(void) {
|
||||
return 256;
|
||||
}
|
||||
#define SPIFLASH_SECTOR_SIZE 65536
|
||||
static inline int spiflash_sector_size_read(void) {
|
||||
return 65536;
|
||||
}
|
||||
#define ROM_DISABLE 1
|
||||
static inline int rom_disable_read(void) {
|
||||
return 1;
|
||||
}
|
||||
#define CONFIG_CLOCK_FREQUENCY 12000000
|
||||
static inline int config_clock_frequency_read(void) {
|
||||
return 12000000;
|
||||
}
|
||||
#define CONFIG_CPU_RESET_ADDR 0
|
||||
static inline int config_cpu_reset_addr_read(void) {
|
||||
return 0;
|
||||
}
|
||||
#define CONFIG_CPU_TYPE "VEXRISCV"
|
||||
static inline const char * config_cpu_type_read(void) {
|
||||
return "VEXRISCV";
|
||||
}
|
||||
#define CONFIG_CPU_VARIANT "VEXRISCV"
|
||||
static inline const char * config_cpu_variant_read(void) {
|
||||
return "VEXRISCV";
|
||||
}
|
||||
#define CONFIG_CSR_DATA_WIDTH 8
|
||||
static inline int config_csr_data_width_read(void) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
#endif
|
43
src/main.c
43
src/main.c
|
@ -1,43 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <printf.h>
|
||||
#include <uart.h>
|
||||
#include <irq.h>
|
||||
|
||||
#include <generated/csr.h>
|
||||
|
||||
void isr(void) {
|
||||
unsigned int irqs;
|
||||
|
||||
irqs = irq_pending() & irq_getmask();
|
||||
|
||||
if(irqs & (1 << UART_INTERRUPT))
|
||||
uart_isr();
|
||||
}
|
||||
|
||||
static void rv_putchar(void *ignored, char c) {
|
||||
(void)ignored;
|
||||
uart_write(c);
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
irq_setmask(0);
|
||||
irq_setie(1);
|
||||
uart_init();
|
||||
init_printf(NULL, rv_putchar);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
init();
|
||||
|
||||
int i = 0;
|
||||
printf("Hello, world!\n");
|
||||
while (1) {
|
||||
printf("10 PRINT HELLO, WORLD\n");
|
||||
printf("20 GOTO 10\n");
|
||||
printf("i: %d\n", i++);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
GIT_VERSION := $(shell git describe --tags)
|
||||
TRGT ?= riscv64-unknown-elf-
|
||||
CC := $(TRGT)gcc
|
||||
CXX := $(TRGT)g++
|
||||
OBJCOPY := $(TRGT)objcopy
|
||||
|
||||
RM := rm -rf
|
||||
COPY := cp -a
|
||||
PATH_SEP := /
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
COPY := copy
|
||||
RM := del
|
||||
PATH_SEP := \\
|
||||
endif
|
||||
|
||||
ifeq ($(LITEX),1)
|
||||
BASE_DIR := ../../../../sw
|
||||
LDSCRIPT := $(BASE_DIR)/ld/linker.ld
|
||||
LD_DIR := ../include/generated
|
||||
ADD_CFLAGS := -I../include -I$(BASE_DIR)/include -D__vexriscv__ -march=rv32im -mabi=ilp32
|
||||
ADD_LFLAGS :=
|
||||
PACKAGE := bios
|
||||
else
|
||||
BASE_DIR := .
|
||||
LD_DIR := $(BASE_DIR)/ld
|
||||
LDSCRIPT := $(BASE_DIR)/ld/linker.ld
|
||||
ADD_CFLAGS := -I$(BASE_DIR)/include -D__vexriscv__ -march=rv32im -mabi=ilp32
|
||||
ADD_LFLAGS :=
|
||||
PACKAGE := foboot
|
||||
endif
|
||||
|
||||
LDSCRIPTS := $(LDSCRIPT) $(LD_DIR)/output_format.ld $(LD_DIR)/regions.ld
|
||||
SRC_DIR := $(BASE_DIR)/src
|
||||
THIRD_PARTY := $(BASE_DIR)/third_party
|
||||
DBG_CFLAGS := -ggdb -g -DDEBUG -Wall
|
||||
DBG_LFLAGS := -ggdb -g -Wall
|
||||
CFLAGS := $(ADD_CFLAGS) \
|
||||
-Wall -Wextra \
|
||||
-ffunction-sections -fdata-sections -fno-common \
|
||||
-fomit-frame-pointer -Os \
|
||||
-march=rv32i \
|
||||
-DGIT_VERSION=u\"$(GIT_VERSION)\" -std=gnu11
|
||||
CXXFLAGS := $(CFLAGS) -std=c++11 -fno-rtti -fno-exceptions
|
||||
LFLAGS := $(CFLAGS) $(ADD_LFLAGS) -L$(LD_DIR) \
|
||||
-nostartfiles \
|
||||
-nostdlib \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,--no-warn-mismatch \
|
||||
-Wl,--script=$(LDSCRIPT) \
|
||||
-Wl,--build-id=none
|
||||
|
||||
OBJ_DIR := .obj
|
||||
|
||||
CSOURCES := $(wildcard $(SRC_DIR)/*.c) $(wildcard $(THIRD_PARTY)/libbase/*.c) $(wildcard $(THIRD_PARTY)/*.c)
|
||||
CPPSOURCES := $(wildcard $(SRC_DIR)/*.cpp) $(wildcard $(THIRD_PARTY)/libbase/*.cpp) $(wildcard $(THIRD_PARTY)/*.cpp)
|
||||
ASOURCES := $(wildcard $(SRC_DIR)/*.S) $(wildcard $(THIRD_PARTY)/libbase/*.S) $(wildcard $(THIRD_PARTY)/*.S)
|
||||
COBJS := $(addprefix $(OBJ_DIR)/, $(notdir $(CSOURCES:.c=.o)))
|
||||
CXXOBJS := $(addprefix $(OBJ_DIR)/, $(notdir $(CPPSOURCES:.cpp=.o)))
|
||||
AOBJS := $(addprefix $(OBJ_DIR)/, $(notdir $(ASOURCES:.S=.o)))
|
||||
OBJECTS := $(COBJS) $(CXXOBJS) $(AOBJS)
|
||||
VPATH := $(SRC_DIR) $(THIRD_PARTY) $(THIRD_PARTY)/libbase
|
||||
|
||||
QUIET := @
|
||||
|
||||
ALL := all
|
||||
TARGET := $(PACKAGE).elf
|
||||
CLEAN := clean
|
||||
|
||||
$(ALL): $(TARGET) $(PACKAGE).bin $(PACKAGE).ihex
|
||||
|
||||
$(OBJECTS): | $(OBJ_DIR)
|
||||
|
||||
$(TARGET): $(OBJECTS) $(LDSCRIPTS)
|
||||
$(QUIET) echo " LD $@"
|
||||
$(QUIET) $(CC) $(OBJECTS) $(LFLAGS) -o $@
|
||||
|
||||
$(PACKAGE).bin: $(TARGET)
|
||||
$(QUIET) echo " OBJCOPY $@"
|
||||
$(QUIET) $(OBJCOPY) -O binary $(TARGET) $@
|
||||
|
||||
$(PACKAGE).dfu: $(TARGET)
|
||||
$(QUIET) echo " DFU $@"
|
||||
$(QUIET) $(COPY) $(PACKAGE).bin $@
|
||||
$(QUIET) dfu-suffix -v 1209 -p 70b1 -a $@
|
||||
|
||||
$(PACKAGE).ihex: $(TARGET)
|
||||
$(QUIET) echo " IHEX $(PACKAGE).ihex"
|
||||
$(QUIET) $(OBJCOPY) -O ihex $(TARGET) $@
|
||||
|
||||
$(DEBUG): CFLAGS += $(DBG_CFLAGS)
|
||||
$(DEBUG): LFLAGS += $(DBG_LFLAGS)
|
||||
CFLAGS += $(DBG_CFLAGS)
|
||||
LFLAGS += $(DBG_LFLAGS)
|
||||
$(DEBUG): $(TARGET)
|
||||
|
||||
$(OBJ_DIR):
|
||||
$(QUIET) mkdir $(OBJ_DIR)
|
||||
|
||||
$(COBJS) : $(OBJ_DIR)/%.o : %.c $(BASE_DIR)/Makefile
|
||||
$(QUIET) echo " CC $< $(notdir $@)"
|
||||
$(QUIET) $(CC) -c $< $(CFLAGS) -o $@ -MMD
|
||||
|
||||
$(OBJ_DIR)/%.o: %.cpp
|
||||
$(QUIET) echo " CXX $< $(notdir $@)"
|
||||
$(QUIET) $(CXX) -c $< $(CXXFLAGS) -o $@ -MMD
|
||||
|
||||
$(OBJ_DIR)/%.o: %.S
|
||||
$(QUIET) echo " AS $< $(notdir $@)"
|
||||
$(QUIET) $(CC) -x assembler-with-cpp -c $< $(CFLAGS) -o $@ -MMD
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(QUIET) echo " RM $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))"
|
||||
-$(QUIET) $(RM) $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))
|
||||
$(QUIET) echo " RM $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))"
|
||||
-$(QUIET) $(RM) $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.o))
|
||||
$(QUIET) echo " RM $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol $(PACKAGE).ihex $(PACKAGE).dfu"
|
||||
-$(QUIET) $(RM) $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol $(PACKAGE).ihex $(PACKAGE).dfu
|
||||
|
||||
include $(wildcard $(OBJ_DIR)/*.d)
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef _DFU_H
|
||||
#define _DFU_H
|
||||
|
||||
/*
|
||||
* Fadecandy DFU Bootloader
|
||||
*
|
||||
* Copyright (c) 2013 Micah Elizabeth Scott
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
appIDLE = 0,
|
||||
appDETACH,
|
||||
dfuIDLE,
|
||||
dfuDNLOAD_SYNC,
|
||||
dfuDNBUSY,
|
||||
dfuDNLOAD_IDLE,
|
||||
dfuMANIFEST_SYNC,
|
||||
dfuMANIFEST,
|
||||
dfuMANIFEST_WAIT_RESET,
|
||||
dfuUPLOAD_IDLE,
|
||||
dfuERROR
|
||||
} dfu_state_t;
|
||||
|
||||
typedef enum {
|
||||
OK = 0,
|
||||
errTARGET,
|
||||
errFILE,
|
||||
errWRITE,
|
||||
errERASE,
|
||||
errCHECK_ERASED,
|
||||
errPROG,
|
||||
errVERIFY,
|
||||
errADDRESS,
|
||||
errNOTDONE,
|
||||
errFIRMWARE,
|
||||
errVENDOR,
|
||||
errUSBR,
|
||||
errPOR,
|
||||
errUNKNOWN,
|
||||
errSTALLEDPKT,
|
||||
} dfu_status_t;
|
||||
|
||||
#define DFU_INTERFACE 0
|
||||
#define DFU_DETACH_TIMEOUT 10000 // 10 second timer
|
||||
#define DFU_TRANSFER_SIZE 256 // Flash sector size
|
||||
|
||||
// Main thread
|
||||
void dfu_init();
|
||||
|
||||
void dfu_poll(void);
|
||||
|
||||
// USB entry points. Always successful.
|
||||
uint8_t dfu_getstate();
|
||||
|
||||
// USB entry points. True on success, false for stall.
|
||||
bool dfu_getstatus(uint8_t status[8]);
|
||||
bool dfu_clrstatus();
|
||||
bool dfu_abort();
|
||||
bool dfu_download(unsigned blockNum, unsigned blockLength,
|
||||
unsigned packetOffset, unsigned packetLength, const uint8_t *data);
|
||||
|
||||
#endif /* _DFU_H */
|
|
@ -0,0 +1,416 @@
|
|||
#ifndef __GENERATED_CSR_H
|
||||
#define __GENERATED_CSR_H
|
||||
#include <stdint.h>
|
||||
#ifdef CSR_ACCESSORS_DEFINED
|
||||
extern void csr_writeb(uint8_t value, uint32_t addr);
|
||||
extern uint8_t csr_readb(uint32_t addr);
|
||||
extern void csr_writew(uint16_t value, uint32_t addr);
|
||||
extern uint16_t csr_readw(uint32_t addr);
|
||||
extern void csr_writel(uint32_t value, uint32_t addr);
|
||||
extern uint32_t csr_readl(uint32_t addr);
|
||||
#else /* ! CSR_ACCESSORS_DEFINED */
|
||||
#include <hw/common.h>
|
||||
#endif /* ! CSR_ACCESSORS_DEFINED */
|
||||
|
||||
/* bbspi */
|
||||
#define CSR_BBSPI_BASE 0xe0005000
|
||||
#define CSR_BBSPI_DO_ADDR 0xe0005000
|
||||
#define CSR_BBSPI_DO_SIZE 1
|
||||
static inline unsigned char bbspi_do_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005000);
|
||||
return r;
|
||||
}
|
||||
static inline void bbspi_do_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005000);
|
||||
}
|
||||
#define CSR_BBSPI_OE_ADDR 0xe0005004
|
||||
#define CSR_BBSPI_OE_SIZE 1
|
||||
static inline unsigned char bbspi_oe_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005004);
|
||||
return r;
|
||||
}
|
||||
static inline void bbspi_oe_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005004);
|
||||
}
|
||||
#define CSR_BBSPI_DI_ADDR 0xe0005008
|
||||
#define CSR_BBSPI_DI_SIZE 1
|
||||
static inline unsigned char bbspi_di_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005008);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ctrl */
|
||||
#define CSR_CTRL_BASE 0xe0000000
|
||||
#define CSR_CTRL_RESET_ADDR 0xe0000000
|
||||
#define CSR_CTRL_RESET_SIZE 1
|
||||
static inline unsigned char ctrl_reset_read(void) {
|
||||
unsigned char r = csr_readl(0xe0000000);
|
||||
return r;
|
||||
}
|
||||
static inline void ctrl_reset_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0000000);
|
||||
}
|
||||
#define CSR_CTRL_SCRATCH_ADDR 0xe0000004
|
||||
#define CSR_CTRL_SCRATCH_SIZE 4
|
||||
static inline unsigned int ctrl_scratch_read(void) {
|
||||
unsigned int r = csr_readl(0xe0000004);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0000008);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000000c);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0000010);
|
||||
return r;
|
||||
}
|
||||
static inline void ctrl_scratch_write(unsigned int value) {
|
||||
csr_writel(value >> 24, 0xe0000004);
|
||||
csr_writel(value >> 16, 0xe0000008);
|
||||
csr_writel(value >> 8, 0xe000000c);
|
||||
csr_writel(value, 0xe0000010);
|
||||
}
|
||||
#define CSR_CTRL_BUS_ERRORS_ADDR 0xe0000014
|
||||
#define CSR_CTRL_BUS_ERRORS_SIZE 4
|
||||
static inline unsigned int ctrl_bus_errors_read(void) {
|
||||
unsigned int r = csr_readl(0xe0000014);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0000018);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000001c);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0000020);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* reboot */
|
||||
#define CSR_REBOOT_BASE 0xe0005800
|
||||
#define CSR_REBOOT_CTRL_ADDR 0xe0005800
|
||||
#define CSR_REBOOT_CTRL_SIZE 1
|
||||
static inline unsigned char reboot_ctrl_read(void) {
|
||||
unsigned char r = csr_readl(0xe0005800);
|
||||
return r;
|
||||
}
|
||||
static inline void reboot_ctrl_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0005800);
|
||||
}
|
||||
|
||||
/* rgb */
|
||||
#define CSR_RGB_BASE 0xe0006000
|
||||
#define CSR_RGB_DAT_ADDR 0xe0006000
|
||||
#define CSR_RGB_DAT_SIZE 1
|
||||
static inline unsigned char rgb_dat_read(void) {
|
||||
unsigned char r = csr_readl(0xe0006000);
|
||||
return r;
|
||||
}
|
||||
static inline void rgb_dat_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0006000);
|
||||
}
|
||||
#define CSR_RGB_ADDR_ADDR 0xe0006004
|
||||
#define CSR_RGB_ADDR_SIZE 1
|
||||
static inline unsigned char rgb_addr_read(void) {
|
||||
unsigned char r = csr_readl(0xe0006004);
|
||||
return r;
|
||||
}
|
||||
static inline void rgb_addr_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0006004);
|
||||
}
|
||||
#define CSR_RGB_CTRL_ADDR 0xe0006008
|
||||
#define CSR_RGB_CTRL_SIZE 1
|
||||
static inline unsigned char rgb_ctrl_read(void) {
|
||||
unsigned char r = csr_readl(0xe0006008);
|
||||
return r;
|
||||
}
|
||||
static inline void rgb_ctrl_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0006008);
|
||||
}
|
||||
|
||||
/* timer0 */
|
||||
#define CSR_TIMER0_BASE 0xe0002800
|
||||
#define CSR_TIMER0_LOAD_ADDR 0xe0002800
|
||||
#define CSR_TIMER0_LOAD_SIZE 4
|
||||
static inline unsigned int timer0_load_read(void) {
|
||||
unsigned int r = csr_readl(0xe0002800);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002804);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002808);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000280c);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_load_write(unsigned int value) {
|
||||
csr_writel(value >> 24, 0xe0002800);
|
||||
csr_writel(value >> 16, 0xe0002804);
|
||||
csr_writel(value >> 8, 0xe0002808);
|
||||
csr_writel(value, 0xe000280c);
|
||||
}
|
||||
#define CSR_TIMER0_RELOAD_ADDR 0xe0002810
|
||||
#define CSR_TIMER0_RELOAD_SIZE 4
|
||||
static inline unsigned int timer0_reload_read(void) {
|
||||
unsigned int r = csr_readl(0xe0002810);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002814);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002818);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000281c);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_reload_write(unsigned int value) {
|
||||
csr_writel(value >> 24, 0xe0002810);
|
||||
csr_writel(value >> 16, 0xe0002814);
|
||||
csr_writel(value >> 8, 0xe0002818);
|
||||
csr_writel(value, 0xe000281c);
|
||||
}
|
||||
#define CSR_TIMER0_EN_ADDR 0xe0002820
|
||||
#define CSR_TIMER0_EN_SIZE 1
|
||||
static inline unsigned char timer0_en_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002820);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_en_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002820);
|
||||
}
|
||||
#define CSR_TIMER0_UPDATE_VALUE_ADDR 0xe0002824
|
||||
#define CSR_TIMER0_UPDATE_VALUE_SIZE 1
|
||||
static inline unsigned char timer0_update_value_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002824);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_update_value_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002824);
|
||||
}
|
||||
#define CSR_TIMER0_VALUE_ADDR 0xe0002828
|
||||
#define CSR_TIMER0_VALUE_SIZE 4
|
||||
static inline unsigned int timer0_value_read(void) {
|
||||
unsigned int r = csr_readl(0xe0002828);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe000282c);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002830);
|
||||
r <<= 8;
|
||||
r |= csr_readl(0xe0002834);
|
||||
return r;
|
||||
}
|
||||
#define CSR_TIMER0_EV_STATUS_ADDR 0xe0002838
|
||||
#define CSR_TIMER0_EV_STATUS_SIZE 1
|
||||
static inline unsigned char timer0_ev_status_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002838);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_ev_status_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002838);
|
||||
}
|
||||
#define CSR_TIMER0_EV_PENDING_ADDR 0xe000283c
|
||||
#define CSR_TIMER0_EV_PENDING_SIZE 1
|
||||
static inline unsigned char timer0_ev_pending_read(void) {
|
||||
unsigned char r = csr_readl(0xe000283c);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_ev_pending_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000283c);
|
||||
}
|
||||
#define CSR_TIMER0_EV_ENABLE_ADDR 0xe0002840
|
||||
#define CSR_TIMER0_EV_ENABLE_SIZE 1
|
||||
static inline unsigned char timer0_ev_enable_read(void) {
|
||||
unsigned char r = csr_readl(0xe0002840);
|
||||
return r;
|
||||
}
|
||||
static inline void timer0_ev_enable_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0002840);
|
||||
}
|
||||
|
||||
/* usb */
|
||||
#define CSR_USB_BASE 0xe0004800
|
||||
#define CSR_USB_PULLUP_OUT_ADDR 0xe0004800
|
||||
#define CSR_USB_PULLUP_OUT_SIZE 1
|
||||
static inline unsigned char usb_pullup_out_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004800);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_pullup_out_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004800);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_EV_STATUS_ADDR 0xe0004804
|
||||
#define CSR_USB_EP_0_OUT_EV_STATUS_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_ev_status_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004804);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_out_ev_status_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004804);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_EV_PENDING_ADDR 0xe0004808
|
||||
#define CSR_USB_EP_0_OUT_EV_PENDING_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_ev_pending_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004808);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_out_ev_pending_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004808);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_EV_ENABLE_ADDR 0xe000480c
|
||||
#define CSR_USB_EP_0_OUT_EV_ENABLE_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_ev_enable_read(void) {
|
||||
unsigned char r = csr_readl(0xe000480c);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_out_ev_enable_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000480c);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_LAST_TOK_ADDR 0xe0004810
|
||||
#define CSR_USB_EP_0_OUT_LAST_TOK_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_last_tok_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004810);
|
||||
return r;
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_RESPOND_ADDR 0xe0004814
|
||||
#define CSR_USB_EP_0_OUT_RESPOND_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_respond_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004814);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_out_respond_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004814);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_DTB_ADDR 0xe0004818
|
||||
#define CSR_USB_EP_0_OUT_DTB_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_dtb_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004818);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_out_dtb_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004818);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_OBUF_HEAD_ADDR 0xe000481c
|
||||
#define CSR_USB_EP_0_OUT_OBUF_HEAD_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_obuf_head_read(void) {
|
||||
unsigned char r = csr_readl(0xe000481c);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_out_obuf_head_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000481c);
|
||||
}
|
||||
#define CSR_USB_EP_0_OUT_OBUF_EMPTY_ADDR 0xe0004820
|
||||
#define CSR_USB_EP_0_OUT_OBUF_EMPTY_SIZE 1
|
||||
static inline unsigned char usb_ep_0_out_obuf_empty_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004820);
|
||||
return r;
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_EV_STATUS_ADDR 0xe0004824
|
||||
#define CSR_USB_EP_0_IN_EV_STATUS_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_ev_status_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004824);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_in_ev_status_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004824);
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_EV_PENDING_ADDR 0xe0004828
|
||||
#define CSR_USB_EP_0_IN_EV_PENDING_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_ev_pending_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004828);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_in_ev_pending_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004828);
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_EV_ENABLE_ADDR 0xe000482c
|
||||
#define CSR_USB_EP_0_IN_EV_ENABLE_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_ev_enable_read(void) {
|
||||
unsigned char r = csr_readl(0xe000482c);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_in_ev_enable_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000482c);
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_LAST_TOK_ADDR 0xe0004830
|
||||
#define CSR_USB_EP_0_IN_LAST_TOK_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_last_tok_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004830);
|
||||
return r;
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_RESPOND_ADDR 0xe0004834
|
||||
#define CSR_USB_EP_0_IN_RESPOND_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_respond_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004834);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_in_respond_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004834);
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_DTB_ADDR 0xe0004838
|
||||
#define CSR_USB_EP_0_IN_DTB_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_dtb_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004838);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_in_dtb_write(unsigned char value) {
|
||||
csr_writel(value, 0xe0004838);
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_IBUF_HEAD_ADDR 0xe000483c
|
||||
#define CSR_USB_EP_0_IN_IBUF_HEAD_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_ibuf_head_read(void) {
|
||||
unsigned char r = csr_readl(0xe000483c);
|
||||
return r;
|
||||
}
|
||||
static inline void usb_ep_0_in_ibuf_head_write(unsigned char value) {
|
||||
csr_writel(value, 0xe000483c);
|
||||
}
|
||||
#define CSR_USB_EP_0_IN_IBUF_EMPTY_ADDR 0xe0004840
|
||||
#define CSR_USB_EP_0_IN_IBUF_EMPTY_SIZE 1
|
||||
static inline unsigned char usb_ep_0_in_ibuf_empty_read(void) {
|
||||
unsigned char r = csr_readl(0xe0004840);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* constants */
|
||||
#define NMI_INTERRUPT 0
|
||||
static inline int nmi_interrupt_read(void) {
|
||||
return 0;
|
||||
}
|
||||
#define TIMER0_INTERRUPT 1
|
||||
static inline int timer0_interrupt_read(void) {
|
||||
return 1;
|
||||
}
|
||||
#define UART_INTERRUPT 2
|
||||
static inline int uart_interrupt_read(void) {
|
||||
return 2;
|
||||
}
|
||||
#define USB_INTERRUPT 3
|
||||
static inline int usb_interrupt_read(void) {
|
||||
return 3;
|
||||
}
|
||||
#define CSR_DATA_WIDTH 8
|
||||
static inline int csr_data_width_read(void) {
|
||||
return 8;
|
||||
}
|
||||
#define SYSTEM_CLOCK_FREQUENCY 12000000
|
||||
static inline int system_clock_frequency_read(void) {
|
||||
return 12000000;
|
||||
}
|
||||
#define ROM_DISABLE 1
|
||||
static inline int rom_disable_read(void) {
|
||||
return 1;
|
||||
}
|
||||
#define CONFIG_CLOCK_FREQUENCY 12000000
|
||||
static inline int config_clock_frequency_read(void) {
|
||||
return 12000000;
|
||||
}
|
||||
#define CONFIG_CPU_RESET_ADDR 0
|
||||
static inline int config_cpu_reset_addr_read(void) {
|
||||
return 0;
|
||||
}
|
||||
#define CONFIG_CPU_TYPE "VEXRISCV"
|
||||
static inline const char * config_cpu_type_read(void) {
|
||||
return "VEXRISCV";
|
||||
}
|
||||
#define CONFIG_CPU_VARIANT "VEXRISCV"
|
||||
static inline const char * config_cpu_variant_read(void) {
|
||||
return "VEXRISCV";
|
||||
}
|
||||
#define CONFIG_CSR_DATA_WIDTH 8
|
||||
static inline int config_csr_data_width_read(void) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,13 +1,10 @@
|
|||
#ifndef __GENERATED_MEM_H
|
||||
#define __GENERATED_MEM_H
|
||||
|
||||
#define SPIFLASH_BASE 0x20000000
|
||||
#define SPIFLASH_SIZE 0x00200000
|
||||
|
||||
#define SRAM_BASE 0x10000000
|
||||
#define SRAM_SIZE 0x00020000
|
||||
|
||||
#define ROM_BASE 0x00000000
|
||||
#define ROM_SIZE 0x00002000
|
||||
|
||||
#endif
|
||||
#ifndef __GENERATED_MEM_H
|
||||
#define __GENERATED_MEM_H
|
||||
|
||||
#define SRAM_BASE 0x10000000
|
||||
#define SRAM_SIZE 0x00020000
|
||||
|
||||
#define ROM_BASE 0x00000000
|
||||
#define ROM_SIZE 0x00002000
|
||||
|
||||
#endif
|
|
@ -0,0 +1,602 @@
|
|||
/****************************************************************************
|
||||
* Grainuum Software USB Stack *
|
||||
* *
|
||||
* MIT License: *
|
||||
* Copyright (c) 2016 Sean Cross *
|
||||
* *
|
||||
* 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, distribute with modifications, 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 ABOVE 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. *
|
||||
* *
|
||||
* Except as contained in this notice, the name(s) of the above copyright *
|
||||
* holders shall not be used in advertising or otherwise to promote the *
|
||||
* sale, use or other dealings in this Software without prior written *
|
||||
* authorization. *
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _GRAINUUM_H
|
||||
#define _GRAINUUM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Extra fields for GrainuumState struct.
|
||||
* @note You probably can ignore this.
|
||||
*/
|
||||
#ifndef GRAINUUM_STATE_EXTRA
|
||||
#define GRAINUUM_STATE_EXTRA
|
||||
#endif /* GRAINUUM_STATE_EXTRA */
|
||||
|
||||
#define GRAINUUM_PACKET_SIZE_MAX 64
|
||||
|
||||
/**
|
||||
* @brief Extra fields for GrainuumUSB struct.
|
||||
* @note Use this to store context and thread information.
|
||||
*/
|
||||
#ifndef GRAINUUM_EXTRA
|
||||
#define GRAINUUM_EXTRA
|
||||
#endif /* GRAINUUM_EXTRA */
|
||||
|
||||
#define GET_STATUS 0
|
||||
#define CLEAR_FEATURE 1
|
||||
#define SET_FEATURE 3
|
||||
#define SET_ADDRESS 5
|
||||
#define GET_DESCRIPTOR 6
|
||||
#define SET_DESCRIPTOR 7
|
||||
#define GET_CONFIGURATION 8
|
||||
#define SET_CONFIGURATION 9
|
||||
#define GET_INTERFACE 10
|
||||
#define SET_INTERFACE 11
|
||||
#define SYNC_FRAME 12
|
||||
|
||||
#define GET_REPORT 1
|
||||
#define GET_IDLE 2
|
||||
#define GET_PROTOCOL 3
|
||||
#define SET_REPORT 9
|
||||
#define SET_IDLE 10
|
||||
#define SET_PROTOCOL 11
|
||||
|
||||
enum usb_pids {
|
||||
USB_PID_RESERVED = 0xf0,
|
||||
USB_PID_OUT = 0xe1,
|
||||
USB_PID_ACK = 0xd2,
|
||||
USB_PID_DATA0 = 0xc3,
|
||||
USB_PID_PING = 0xb4,
|
||||
USB_PID_SOF = 0xa5,
|
||||
USB_PID_NYET = 0x96,
|
||||
USB_PID_DATA2 = 0x87,
|
||||
USB_PID_SPLIT = 0x78,
|
||||
USB_PID_IN = 0x69,
|
||||
USB_PID_NAK = 0x5a,
|
||||
USB_PID_DATA1 = 0x4b,
|
||||
USB_PID_ERR = 0x3c,
|
||||
USB_PID_SETUP = 0x2d,
|
||||
USB_PID_STALL = 0x1e,
|
||||
USB_PID_MDATA = 0x0f,
|
||||
};
|
||||
|
||||
enum valenty_usb_pids {
|
||||
VUSB_PID_IN = 0x2,
|
||||
VUSB_PID_OUT = 0x0,
|
||||
VUSB_PID_SETUP = 0x3,
|
||||
};
|
||||
|
||||
struct GrainuumUSB;
|
||||
struct GrainuumState;
|
||||
struct GrainuumConfig;
|
||||
|
||||
/* Function callbacks */
|
||||
|
||||
/* Each of these functions are called by the USB system to get a buffer.
|
||||
* On return, *data will point to the buffer, and the number of bytes
|
||||
* in the buffer will be returned.
|
||||
*
|
||||
* If the data does not exist, return 0.
|
||||
*/
|
||||
typedef int (*get_usb_descriptor_t)(struct GrainuumUSB *usb,
|
||||
const void *pkt,
|
||||
const void **data);
|
||||
typedef void (*usb_set_config_num_t)(struct GrainuumUSB *usb,
|
||||
int configNum);
|
||||
|
||||
/*
|
||||
* Called when doing an OUT xfer (data to device) to get a buffer for
|
||||
* the specified endpoint.
|
||||
* It is up to the user to ensure the buffer is large enough.
|
||||
*/
|
||||
typedef void * (*usb_get_buffer_t)(struct GrainuumUSB *usb,
|
||||
uint8_t epnum,
|
||||
int32_t *size);
|
||||
|
||||
/*
|
||||
* When data is received (i.e. OUT EP), this function will be called.
|
||||
*/
|
||||
typedef int (*usb_data_in_t)(struct GrainuumUSB *usb,
|
||||
uint8_t epnum,
|
||||
uint32_t bytes,
|
||||
const void *data);
|
||||
|
||||
/**
|
||||
* @brief Called immediately after @p grainuumSendData() has queued data.
|
||||
* @note This function can be used to e.g. sleep a thread.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @param[in] epnum endpoint number of the transfer
|
||||
* @param[in] data pointer to the data being written
|
||||
* @param[in] size number of bytes being written
|
||||
* @api
|
||||
*/
|
||||
typedef void (*usb_data_out_start_t)(struct GrainuumUSB *usb,
|
||||
int epnum,
|
||||
const void *data,
|
||||
int size);
|
||||
|
||||
/**
|
||||
* @brief Called once all data has been sent.
|
||||
* @note This function can be used to e.g. wake up a thread.
|
||||
* @param[out] usb pointer to the @p GrainuumUSB object
|
||||
* @param[out] result whether the transfer was successful (0), or had an error.
|
||||
* @api
|
||||
*/
|
||||
typedef int (*usb_data_out_finish_t)(struct GrainuumUSB *usb,
|
||||
int result);
|
||||
|
||||
/* Structure of a USB packet on the wire, plus size field */
|
||||
struct usb_packet {
|
||||
union {
|
||||
struct {
|
||||
// uint8_t pid;
|
||||
uint8_t data[GRAINUUM_PACKET_SIZE_MAX + 2]; /* Including CRC */
|
||||
} __attribute((packed, aligned(4)));
|
||||
uint8_t raw_data[GRAINUUM_PACKET_SIZE_MAX + 2];
|
||||
} __attribute((packed, aligned(4)));
|
||||
uint8_t size; /* Not including pid (so may be 0) */
|
||||
/* Checksum omitted */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/* USB Descriptors */
|
||||
|
||||
#define DT_DEVICE 0x01
|
||||
#define DT_CONFIGURATION 0x02
|
||||
#define DT_STRING 0x03
|
||||
#define DT_INTERFACE 0x04
|
||||
#define DT_ENDPOINT 0x05
|
||||
#define DT_DEVICE_QUALIFIER 0x06
|
||||
#define DT_OTHER_SPEED_CONFIGURATION 0x07
|
||||
#define DT_INTERFACE_POWER 0x08
|
||||
|
||||
#define DT_HID 0x21
|
||||
#define DT_HID_REPORT 0x22
|
||||
#define DT_PID 0x23
|
||||
|
||||
struct usb_setup_packet {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
union {
|
||||
uint16_t wValue;
|
||||
struct {
|
||||
uint8_t wValueL;
|
||||
uint8_t wValueH;
|
||||
};
|
||||
};
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct usb_device_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdUSB;
|
||||
uint8_t bDeviceClass;
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize0;
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct usb_configuration_descriptor {
|
||||
uint8_t bLength; /* Size of this descriptor, in bytes (9) */
|
||||
uint8_t bDescriptorType; /* DT_CONFIGURATION (2) */
|
||||
uint16_t wTotalLength; /* Total length of this, plus sizeof(data) */
|
||||
uint8_t bNumInterfaces; /* Number of interfaces supported by config */
|
||||
uint8_t bConfigurationValue; /* Value used by Set Configuration */
|
||||
uint8_t iConfiguration; /* index of string descriptor for config */
|
||||
uint8_t bmAttributes; /* Bitmap of attributes. D7 must be 1. */
|
||||
uint8_t bMaxPower; /* Maximum power, in units of 2mA */
|
||||
uint8_t data[]; /* Remaining descriptors */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct usb_string_descriptor {
|
||||
uint8_t bLength; /* sizeof(usb_string_descriptor) + sizeof(data) */
|
||||
uint8_t bDescriptorType; /* DT_STRING (3) */
|
||||
uint8_t data[]; /* UTF-16LE string data or lang data(for string 0 */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct usb_interface_descriptor {
|
||||
uint8_t bLength; /* sizeof(usb_interface_descriptor) (9) */
|
||||
uint8_t bDescriptorType; /* DT_INTERFACE (4) */
|
||||
uint8_t bInterfaceNumber; /* Which interface this describes. Usually 0. */
|
||||
uint8_t bAlternateSetting; /* ??? */
|
||||
uint8_t bNumEndpoints; /* Number of endpoints, minus 1 */
|
||||
uint8_t bInterfaceClass; /* Class code */
|
||||
uint8_t bInterfaceSubclass; /* Class sub-code */
|
||||
uint8_t bInterfaceProtocol; /* Protocol code, assigned by USB */
|
||||
uint8_t iInterface; /* Index of string for this interface */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct usb_endpoint_descriptor {
|
||||
uint8_t bLength; /* sizeof(usb_endpoint_descriptor) (7) */
|
||||
uint8_t bDescriptorType; /* DT_ENDPOINT (5) */
|
||||
uint8_t bEndpointAddress; /* High bit 1:IN, 0:OUT. Lower 4-bits are EP# */
|
||||
uint8_t bmAttributes; /* 0=control, 2=bulk, 3=interrupt */
|
||||
uint16_t wMaxPacketSize; /* Max packet size for this EP */
|
||||
uint8_t bInterval; /* Polling rate (in 1ms units) */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct usb_hid_descriptor {
|
||||
uint8_t bLength; /* sizeof(usb_hid_descriptor) (9) */
|
||||
uint8_t bDescriptorType; /* DT_HID (0x21) */
|
||||
uint16_t bcdHID; /* HID class version number, in BCD */
|
||||
uint8_t bCountryCode; /* Target country (usually 0) */
|
||||
uint8_t bNumDescriptors; /* Number of HID class descriptors (usually 1) */
|
||||
uint8_t bReportDescriptorType; /* Report descriptor type (usually 0x22) */
|
||||
uint16_t wReportDescriptorLength; /* Length of the HID/PID report descriptor */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
#define GRAINUUM_BUFFER_ELEMENT_SIZE 12 /* 1 PID, 8 data, 2 CRC16, 1 size */
|
||||
/* grainuum_buffer is aligned such that its first byte is on a word boundary.
|
||||
* This is because the first byte of every packet is a PID, which is
|
||||
* immediately discarded. This leaves the remainder of the packet
|
||||
* word-aligned.
|
||||
*/
|
||||
|
||||
#define GRAINUUM_BUFFER(name, sz) \
|
||||
struct { \
|
||||
uint8_t head; \
|
||||
uint8_t tail; \
|
||||
uint8_t padding; \
|
||||
union { \
|
||||
uint8_t buffer[(sz) * GRAINUUM_BUFFER_ELEMENT_SIZE]; \
|
||||
uint8_t elements[sz][GRAINUUM_BUFFER_ELEMENT_SIZE]; \
|
||||
}; \
|
||||
} name __attribute__((aligned(4))); \
|
||||
uint8_t * name ## _head_ptr;
|
||||
#define GRAINUUM_BUFFER_INIT(name) \
|
||||
do { \
|
||||
(name).head = 0; \
|
||||
(name).tail = 0; \
|
||||
name ## _head_ptr = (name).buffer; \
|
||||
} while(0)
|
||||
#define GRAINUUM_BUFFER_ADVANCE(name) \
|
||||
do { \
|
||||
(name).head += GRAINUUM_BUFFER_ELEMENT_SIZE; \
|
||||
if ((name).head >= sizeof((name).buffer)) \
|
||||
(name).head = 0; \
|
||||
name ## _head_ptr = ((name).buffer + (name).head); \
|
||||
} while(0)
|
||||
#define GRAINUUM_BUFFER_TOP(name) \
|
||||
(&((name).buffer[(name).tail]))
|
||||
#define GRAINUUM_BUFFER_REMOVE(name) \
|
||||
do { \
|
||||
(name).tail += GRAINUUM_BUFFER_ELEMENT_SIZE; \
|
||||
if ((name).tail >= sizeof((name).buffer)) \
|
||||
(name).tail = 0; \
|
||||
} while(0)
|
||||
#define GRAINUUM_BUFFER_IS_EMPTY(name) \
|
||||
((name).head == (name).tail)
|
||||
#define GRAINUUM_BUFFER_ENTRY(name) \
|
||||
name ## _head_ptr
|
||||
|
||||
/* Grainuum Structs */
|
||||
|
||||
struct GrainuumConfig {
|
||||
get_usb_descriptor_t getDescriptor;
|
||||
usb_set_config_num_t setConfigNum;
|
||||
usb_get_buffer_t getReceiveBuffer;
|
||||
usb_data_in_t receiveData;
|
||||
usb_data_out_start_t sendDataStarted;
|
||||
usb_data_out_finish_t sendDataFinished;
|
||||
void *data;
|
||||
struct GrainuumUSB *usb;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct GrainuumState {
|
||||
struct GrainuumUSB *usb;
|
||||
|
||||
const void *data_out; /* Pointer to the data that's being sent */
|
||||
int32_t data_out_left; /* How much data has yet to be sent */
|
||||
int32_t data_out_max; /* The maximum number of bytes to send */
|
||||
int32_t data_out_epnum; /* Which endpoint the data is for */
|
||||
|
||||
struct usb_packet packet; /* Currently-queued packet */
|
||||
int packet_queued; /* Whether a packet is queued */
|
||||
|
||||
uint32_t tok_pos; /* Position within the current token */
|
||||
void *tok_buf; /* Buffer storing current token's data */
|
||||
uint8_t tok_epnum; /* Last token's endpoint */
|
||||
|
||||
uint8_t data_buffer; /* Whether we're sending DATA0 or DATA1 */
|
||||
uint8_t packet_type; /* PACKET_SETUP, PACKET_IN, or PACKET_OUT */
|
||||
|
||||
uint8_t address; /* Our configured address */
|
||||
|
||||
GRAINUUM_STATE_EXTRA
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct GrainuumUSB {
|
||||
|
||||
struct GrainuumConfig *cfg; /* Callbacks */
|
||||
int initialized;
|
||||
|
||||
/* USB D- pin specification */
|
||||
uint32_t usbdnIAddr;
|
||||
uint32_t usbdnSAddr;
|
||||
uint32_t usbdnCAddr;
|
||||
uint32_t usbdnDAddr;
|
||||
uint32_t usbdnShift;
|
||||
|
||||
/* USB D+ pin specification */
|
||||
uint32_t usbdpIAddr;
|
||||
uint32_t usbdpSAddr;
|
||||
uint32_t usbdpCAddr;
|
||||
uint32_t usbdpDAddr;
|
||||
uint32_t usbdpShift;
|
||||
|
||||
uint32_t usbdnMask;
|
||||
uint32_t usbdpMask;
|
||||
|
||||
uint32_t queued_size;
|
||||
uint32_t queued_epnum;
|
||||
const void *queued_data;
|
||||
|
||||
struct GrainuumState state; /* Associated state */
|
||||
|
||||
GRAINUUM_EXTRA
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline void grainuumWritel(uint32_t value, uint32_t addr)
|
||||
{
|
||||
*((volatile uint32_t *)addr) = value;
|
||||
}
|
||||
|
||||
static inline uint32_t grainuumReadl(uint32_t addr)
|
||||
{
|
||||
return *(volatile uint32_t *)addr;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Weak hook functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Called just before the USB device is plugged in.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @api
|
||||
*/
|
||||
void grainuumConnectPre(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Called just after the USB device is plugged in.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @api
|
||||
*/
|
||||
void grainuumConnectPost(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Called just before the USB device is unplugged.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @api
|
||||
*/
|
||||
void grainuumDisconnectPre(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Called just after the USB device is unplugged.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @api
|
||||
*/
|
||||
void grainuumDisconnectPost(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Called just before the USB device is first initialized.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @api
|
||||
*/
|
||||
void grainuumInitPre(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Called just before the USB device is first initialized.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @api
|
||||
*/
|
||||
void grainuumInitPost(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Called immediately after a packet has been received.
|
||||
* @note This is called from an interrupt context. Data will
|
||||
* be stored in the buffer that was passed to @p grainuumCaptureI()
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object
|
||||
* @iclass
|
||||
*/
|
||||
void grainuumReceivePacket(struct GrainuumUSB *usb);
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Returns nonzero if Grainuum has been initialized.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @return nonzero if @p GrainuumUSB is initialized.
|
||||
* @retval 0 Object is not initilized.
|
||||
* @api
|
||||
*/
|
||||
int grainuumInitialized(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Queues some data to be sent to the host.
|
||||
* @note After the first 8 bytes, @p data must remain valid
|
||||
* until the transfer has completed. This generally
|
||||
* means you can send const data stored in the text
|
||||
* section, or small 8-byte packets.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @param[in] epnum endpoint number of the transfer.
|
||||
* @param[in] data pointer to the data being written.
|
||||
* @param[in] size number of bytes being written.
|
||||
* @return 0 if the transfer completed successfully.
|
||||
* @retval 0 Transfer completed successfully.
|
||||
* @api
|
||||
*/
|
||||
int grainuumSendData(struct GrainuumUSB *usb, int epnum, const void *data, int size);
|
||||
|
||||
/**
|
||||
* @brief Clears the send buffer, if not empty.
|
||||
* @note If data has already been queued for the PHY, then
|
||||
* this will not prevent it from being sent.
|
||||
* This function is intended to be used to prevent
|
||||
* grainuumSendData() from returning -EAGAIN.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @api
|
||||
*/
|
||||
void grainuumDropData(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Determines if data is already queued.
|
||||
* @note If data has been queued, then this will return
|
||||
* nonzero. If this returns zero, then you can
|
||||
* trust grainuumSendData() will succeed.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @return Nonzero if data is already queued.
|
||||
* @api
|
||||
*/
|
||||
int grainuumDataQueued(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Process one received packet through the Grainuum state machine.
|
||||
* @note This feeds USB packets into the state machine. It should not
|
||||
* be called as part of an interrupt.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @param[in] packet The USB packet that was most recently received, with byte 12 holding the size.
|
||||
* @api
|
||||
*/
|
||||
void grainuumProcess(struct GrainuumUSB *usb,
|
||||
uint8_t pid,
|
||||
const uint8_t packet[12],
|
||||
uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Initialize the Grainuum USB system.
|
||||
* @note This is meant to run as part of an interrupt. Pass
|
||||
* the storage buffer in as @p samples. The number
|
||||
* of bytes that were read will be stored in the last
|
||||
* byte of the array. For best performance, make
|
||||
* sure that @p sample is on byte 3 of a 4-byte boundary,
|
||||
* so that samples[1] is on a word boundary. The @p GrainuumUSB
|
||||
* object will start out disconnected.
|
||||
* @param[in] usb Pointer to the @p GrainuumUSB object to initialize.
|
||||
* @param[in] link Pointer to the @p GrainuumConfig object to use.
|
||||
* @api
|
||||
*/
|
||||
void grainuumInit(struct GrainuumUSB *usb, struct GrainuumConfig *link);
|
||||
|
||||
/**
|
||||
* @brief Capture a USB packet from the wire.
|
||||
* @note This is meant to run as part of an interrupt. Pass
|
||||
* the storage buffer in as @p samples. The number
|
||||
* of bytes that were read will be stored in the last
|
||||
* byte of the array. For best performance, make
|
||||
* sure that @p sample is on byte 3 of a 4-byte boundary,
|
||||
* so that samples[1] is on a word boundary.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @param[in] packet Buffer to store the read samples.
|
||||
* @api
|
||||
*/
|
||||
int grainuumCaptureI(struct GrainuumUSB *usb, uint8_t samples[67]);
|
||||
|
||||
/**
|
||||
* @brief Internal function. Queues 8 bytes to be sent by the phy.
|
||||
* @note This is an internal function, and is not meant to be called.
|
||||
* It is meant to queue properly-formatted USB packets complete
|
||||
* with CRC-16 (if required).
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @param[in] epnum The endpoint number to queue data for.
|
||||
* @param[in] buffer The data to queue.
|
||||
* @param[in] size The number of bytes that are queued.
|
||||
* @notapi
|
||||
*/
|
||||
void grainuumWriteQueue(struct GrainuumUSB *usb, int epnum,
|
||||
const void *buffer, int size);
|
||||
|
||||
/**
|
||||
* @brief Simulates plugging the device into USB.
|
||||
* @note All USB Connect hooks will be called.
|
||||
* The default USB state is "disconnected",
|
||||
* so @p grainuumConnect() must be called
|
||||
* to start communications.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @api
|
||||
*/
|
||||
void grainuumConnect(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Simulates unplugging the device from USB.
|
||||
* @note All USB Disconnect hooks will be called.
|
||||
* @param[in] usb pointer to the @p GrainuumUSB object.
|
||||
* @api
|
||||
*/
|
||||
void grainuumDisconnect(struct GrainuumUSB *usb);
|
||||
|
||||
/**
|
||||
* @brief Reads one packet from the wire.
|
||||
* @note This must be called from an interrupt context with
|
||||
* interrupts disabled.
|
||||
* @param[in] usb Pointer to the @p GrainuumUSB object.
|
||||
* @param[out] samples Buffer where the samples will be stored.
|
||||
* @return The number of bytes read, or negative on error
|
||||
* @retval -1 Timeout while reading.
|
||||
* @retval -2 Read too many bits.
|
||||
* @retval -3 Unable to find sync end.
|
||||
* @retval -4 Probably a keepalive packet.
|
||||
* @notapi
|
||||
*/
|
||||
int usbPhyReadI(const struct GrainuumUSB *usb, uint8_t samples[11]);
|
||||
|
||||
/**
|
||||
* @brief Writes one packet from the wire.
|
||||
* @note This must be called from an interrupt context with
|
||||
* interrupts disabled.
|
||||
* @param[in] usb Pointer to the @p GrainuumUSB object.
|
||||
* @param[in] samples Buffer where the samples will be stored.
|
||||
* @param[in] size Number of bytes to write.
|
||||
* @notapi
|
||||
*/
|
||||
void usbPhyWriteI(const struct GrainuumUSB *usb, const void *buffer, uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* _GRAINUUM_H */
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef _RGB_H_
|
||||
#define _RGB_H_
|
||||
|
||||
void rgb_init(void);
|
||||
void rgb_mode_idle(void);
|
||||
void rgb_mode_done(void);
|
||||
void rgb_mode_writing(void);
|
||||
void rgb_mode_error(void);
|
||||
|
||||
#endif /* _RGB_H_ */
|
|
@ -0,0 +1,93 @@
|
|||
#ifndef BB_SPI_H_
|
||||
#define BB_SPI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum spi_state {
|
||||
SS_UNCONFIGURED = 0,
|
||||
SS_SINGLE,
|
||||
SS_DUAL_RX,
|
||||
SS_DUAL_TX,
|
||||
SS_QUAD_RX,
|
||||
SS_QUAD_TX,
|
||||
SS_HARDWARE,
|
||||
};
|
||||
|
||||
enum spi_type {
|
||||
ST_UNCONFIGURED,
|
||||
ST_SINGLE,
|
||||
ST_DUAL,
|
||||
ST_QUAD,
|
||||
ST_QPI,
|
||||
};
|
||||
|
||||
enum spi_pin {
|
||||
SP_MOSI,
|
||||
SP_MISO,
|
||||
SP_HOLD,
|
||||
SP_WP,
|
||||
SP_CS,
|
||||
SP_CLK,
|
||||
SP_D0,
|
||||
SP_D1,
|
||||
SP_D2,
|
||||
SP_D3,
|
||||
};
|
||||
|
||||
struct spi_id {
|
||||
uint8_t manufacturer_id; // Result from 0x90
|
||||
uint8_t device_id; // Result from 0x90
|
||||
uint8_t _manufacturer_id; // Result from 0x9f
|
||||
uint8_t memory_type; // Result from 0x9f
|
||||
uint8_t memory_size; // Result from 0x9f
|
||||
uint8_t signature; // Result from 0xab
|
||||
uint8_t serial[4]; // Result from 0x4b
|
||||
int bytes; // -1 if unknown
|
||||
const char *manufacturer;
|
||||
const char *model;
|
||||
const char *capacity;
|
||||
};
|
||||
|
||||
struct ff_spi;
|
||||
|
||||
void spiPause(struct ff_spi *spi);
|
||||
void spiBegin(struct ff_spi *spi);
|
||||
void spiEnd(struct ff_spi *spi);
|
||||
|
||||
//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);
|
||||
|
||||
struct spi_id spiId(struct ff_spi *spi);
|
||||
void spiOverrideSize(struct ff_spi *spi, 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);
|
||||
|
||||
void spiHold(struct ff_spi *spi);
|
||||
void spiUnhold(struct ff_spi *spi);
|
||||
void spiSwapTxRx(struct ff_spi *spi);
|
||||
|
||||
struct ff_spi *spiAlloc(void);
|
||||
void spiSetPin(struct ff_spi *spi, enum spi_pin pin, int val);
|
||||
void spiFree(struct ff_spi **spi);
|
||||
|
||||
#endif /* BB_SPI_H_ */
|
|
@ -56,4 +56,15 @@ static inline void mtspr(unsigned long add, unsigned long val)
|
|||
}
|
||||
#endif
|
||||
|
||||
#include <generated/csr.h>
|
||||
|
||||
__attribute__((noreturn)) static inline void reboot(void) {
|
||||
reboot_ctrl_write(0xac);
|
||||
while (1);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) static inline void reboot_to_image(uint8_t image_index) {
|
||||
reboot_ctrl_write(0xac | (image_index & 3) << 0);
|
||||
while (1);
|
||||
}
|
||||
#endif /* __SYSTEM_H */
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef __TIME_H
|
||||
#define __TIME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void time_init(void);
|
||||
int elapsed(int *last_event, int period);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TIME_H */
|
|
@ -0,0 +1,109 @@
|
|||
#ifndef TOBOOT_API_H_
|
||||
#define TOBOOT_API_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/// Store this configuration struct at offset 0x94 from the start
|
||||
/// of your binary image.
|
||||
/// You may set all RESERVED values to 0. as they will be calculated
|
||||
/// when the program is written to flash.
|
||||
struct toboot_configuration {
|
||||
/// Set to 0x907070b2 to indicate a valid configuration header.
|
||||
uint32_t magic;
|
||||
|
||||
/// Reserved value. Used as a generational counter. Toboot will
|
||||
/// overwrite this value with a monotonically-increasing counter
|
||||
/// every time a new image is burned. This is used to determine
|
||||
/// which image is the newest.
|
||||
uint16_t reserved_gen;
|
||||
|
||||
/// The starting page for your program in 1024-byte blocks.
|
||||
/// Toboot itself sets this to 0. If this is nonzero, then it
|
||||
/// must be located after the Toboot image. Toboot is currently
|
||||
/// under 5 kilobytes, so make sure this value is at least 6.
|
||||
uint8_t start;
|
||||
|
||||
/// Configuration bitmask. See below for values.
|
||||
uint8_t config;
|
||||
|
||||
/// Set to 0x18349420 to prevent the user from entering Toboot manually.
|
||||
/// Use this value with caution, as it can be used to lockout a Tomu.
|
||||
uint32_t lock_entry;
|
||||
|
||||
/// A bitmask of sectors to erase when updating the program. Each "1"
|
||||
/// causes that sector to be erased, unless it's Toboot itself. Bit values
|
||||
/// determine flash blocks 0-31.
|
||||
uint32_t erase_mask_lo;
|
||||
|
||||
/// A bitmask of sectors to erase when updating the program. Each "1"
|
||||
/// causes that sector to e erased. Use these two values to e.g. force
|
||||
/// private keys to be erased when updating. Bit values determine flash
|
||||
/// blocks 32-63.
|
||||
uint32_t erase_mask_hi;
|
||||
|
||||
/// A hash of the entire header, minus this entry. Toboot calculates
|
||||
/// this when it programs the first block. A Toboot configuration
|
||||
/// header MUST have a valid hash in order to be considered valid.
|
||||
uint32_t reserved_hash;
|
||||
} __attribute__((packed));
|
||||
|
||||
/// Toboot V1.0 leaves IRQs enabled, mimicking the behavior of
|
||||
/// AN0042. Toboot V2.0 makes this configurable by adding a
|
||||
/// bit to the configuration area.
|
||||
#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ_MASK 0x01
|
||||
#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ_SHIFT 0
|
||||
#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ (1 << 0)
|
||||
#define TOBOOT_CONFIG_FLAG_DISABLE_IRQ (0 << 0)
|
||||
|
||||
/// When running a normal program, you won't want Toboot to run.
|
||||
/// However, when developing new software it is handy to have
|
||||
/// Toboot run at poweron, instead of jumping straight to your
|
||||
/// program. Set this flag to enter your program whenever the
|
||||
/// system has just powered on.
|
||||
#define TOBOOT_CONFIG_FLAG_AUTORUN_MASK 0x02
|
||||
#define TOBOOT_CONFIG_FLAG_AUTORUN_SHIFT 1
|
||||
#define TOBOOT_CONFIG_FLAG_AUTORUN (1 << 1)
|
||||
#define TOBOOT_CONFIG_FLAG_AUTORUN_DISABLED (0 << 1)
|
||||
|
||||
/// When we create a fake header, this flag will be set. Otherwise,
|
||||
/// leave the flag cleared. This field has no meaning in user
|
||||
/// applications, and is only used internally.
|
||||
#define TOBOOT_CONFIG_FAKE_MASK 0x04
|
||||
#define TOBOOT_CONFIG_FAKE_SHIFT 2
|
||||
#define TOBOOT_CONFIG_FAKE (1 << 2)
|
||||
|
||||
/// Various magic values describing Toboot configuration headers.
|
||||
#define TOBOOT_V1_MAGIC 0x6fb0
|
||||
#define TOBOOT_V1_MAGIC_MASK 0x0000ffff
|
||||
#define TOBOOT_V2_MAGIC 0x907070b2
|
||||
#define TOBOOT_V2_MAGIC_MASK 0xffffffff
|
||||
|
||||
/// This value is used to prevent manual entry into Toboot.
|
||||
#define TOBOOT_LOCKOUT_MAGIC 0x18349420
|
||||
|
||||
/// The seed value for the hash of Toboot's configuration header
|
||||
#define TOBOOT_HASH_SEED 0x037a5cf1
|
||||
|
||||
/// This is the runtime part that lives at the start of RAM.
|
||||
/// Running programs can use this to force entry into Toboot
|
||||
/// during the next reboot.
|
||||
struct toboot_runtime {
|
||||
/// Set this to 0x74624346 and reboot to enter bootloader,
|
||||
/// even if LOCKOUT is enabled.
|
||||
uint32_t magic;
|
||||
|
||||
/// Set this to 0 when your program starts.
|
||||
uint8_t boot_count;
|
||||
|
||||
/// The bootloader should set this to 0x23 for Tomu.
|
||||
uint8_t board_model;
|
||||
|
||||
/// Unused.
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
/// Set runtime.magic to this value and reboot to force
|
||||
/// entry into Toboot.
|
||||
#define TOBOOT_FORCE_ENTRY_MAGIC 0x74624346
|
||||
|
||||
#endif /* TOBOOT_API_H_ */
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef TOBOOT_INTERNAL_H_
|
||||
#define TOBOOT_INTERNAL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/// This describes the structure that allows the OS to communicate
|
||||
/// with the bootloader. It keeps track of how many times we've
|
||||
/// tried booting, as well as a magic value that tells us to enter
|
||||
/// the bootloader instead of booting the app.
|
||||
/// It also keeps track of the board model.
|
||||
__attribute__((section(".boot_token"))) extern struct toboot_runtime boot_token;
|
||||
|
||||
enum bootloader_reason
|
||||
{
|
||||
NOT_ENTERING_BOOTLOADER = 0,
|
||||
BOOT_TOKEN_PRESENT = 1,
|
||||
BOOT_FAILED_TOO_MANY_TIMES = 2,
|
||||
NO_PROGRAM_PRESENT = 3,
|
||||
BUTTON_HELD_DOWN = 4,
|
||||
COLD_BOOT_CONFIGURATION_FLAG = 5,
|
||||
};
|
||||
|
||||
extern enum bootloader_reason bootloader_reason;
|
||||
|
||||
/// Legacy Toboot V1 configuration values
|
||||
#ifndef TOBOOT_V1_CFG_FLAGS
|
||||
#define TOBOOT_V1_CFG_FLAGS 0
|
||||
#endif
|
||||
#define TOBOOT_V1_CFG_MAGIC_MASK 0xffff
|
||||
#define TOBOOT_V1_CFG_MAGIC 0x70b0
|
||||
|
||||
#ifndef TOBOOT_V1_APP_FLAGS
|
||||
#define TOBOOT_V1_APP_FLAGS 0
|
||||
#endif
|
||||
|
||||
#define TOBOOT_V1_APP_MAGIC_MASK 0xffff
|
||||
#define TOBOOT_V1_APP_MAGIC 0x6fb0
|
||||
#define TOBOOT_V1_APP_PAGE_MASK 0x00ff0000
|
||||
#define TOBOOT_V1_APP_PAGE_SHIFT 16
|
||||
|
||||
uint32_t tb_first_free_address(void);
|
||||
uint32_t tb_first_free_sector(void);
|
||||
const struct toboot_configuration *tb_get_config(void);
|
||||
uint32_t tb_config_hash(const struct toboot_configuration *cfg);
|
||||
void tb_sign_config(struct toboot_configuration *cfg);
|
||||
uint32_t tb_generation(const struct toboot_configuration *cfg);
|
||||
int tb_valid_signature_at_page(uint32_t page);
|
||||
|
||||
#endif /* TOBOOT_INTERNAL_H_ */
|
|
@ -0,0 +1,95 @@
|
|||
/* Teensyduino Core Library
|
||||
* http://www.pjrc.com/teensy/
|
||||
* Copyright (c) 2013 PJRC.COM, LLC.
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* 1. The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 2. If the Software is incorporated into a build system that allows
|
||||
* selection among a list of target devices, then similar target
|
||||
* devices manufactured by PJRC.COM must be included in the list of
|
||||
* target devices and selectable in the same manner.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _usb_desc_h_
|
||||
#define _usb_desc_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <dfu.h>
|
||||
#include <webusb-defs.h>
|
||||
|
||||
struct usb_setup_request {
|
||||
union {
|
||||
struct {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
};
|
||||
uint16_t wRequestAndType;
|
||||
};
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
};
|
||||
|
||||
struct usb_string_descriptor_struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wString[];
|
||||
};
|
||||
|
||||
#define NUM_USB_BUFFERS 8
|
||||
#define VENDOR_ID 0x1209 // pid.codes
|
||||
#define PRODUCT_ID 0x70b1 // Assigned to Tomu project
|
||||
#define DEVICE_VER 0x0101 // Bootloader version
|
||||
#define MANUFACTURER_NAME u"Kosagi"
|
||||
#define MANUFACTURER_NAME_LEN sizeof(MANUFACTURER_NAME)
|
||||
#define PRODUCT_NAME u"Fomu Bootloader (0) " GIT_VERSION
|
||||
#define PRODUCT_NAME_LEN sizeof(PRODUCT_NAME)
|
||||
#define EP0_SIZE 64
|
||||
#define NUM_INTERFACE 1
|
||||
#define CONFIG_DESC_SIZE (9+9+9)
|
||||
|
||||
// Microsoft Compatible ID Feature Descriptor
|
||||
#define MSFT_VENDOR_CODE '~' // Arbitrary, but should be printable ASCII
|
||||
#define MSFT_WCID_LEN 40
|
||||
extern const uint8_t usb_microsoft_wcid[MSFT_WCID_LEN];
|
||||
|
||||
typedef struct {
|
||||
uint16_t wValue;
|
||||
uint16_t length;
|
||||
const uint8_t *addr;
|
||||
} usb_descriptor_list_t;
|
||||
|
||||
extern const usb_descriptor_list_t usb_descriptor_list[];
|
||||
|
||||
// WebUSB Landing page URL descriptor
|
||||
#define WEBUSB_VENDOR_CODE 2
|
||||
|
||||
#ifndef LANDING_PAGE_URL
|
||||
#define LANDING_PAGE_URL "dfu.tomu.im"
|
||||
#endif
|
||||
|
||||
#define LANDING_PAGE_DESCRIPTOR_SIZE (WEBUSB_DT_URL_DESCRIPTOR_SIZE \
|
||||
+ sizeof(LANDING_PAGE_URL) - 1)
|
||||
|
||||
extern const struct webusb_url_descriptor landing_url_descriptor;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef __USB_H
|
||||
#define __USB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct usb_device;
|
||||
struct usb_setup_request;
|
||||
|
||||
void usb_isr(void);
|
||||
void usb_init(void);
|
||||
void usb_connect(void);
|
||||
void usb_disconnect(void);
|
||||
|
||||
int usb_irq_happened(void);
|
||||
void usb_setup(struct usb_device *dev, const struct usb_setup_request *setup);
|
||||
int usb_send(struct usb_device *dev, int epnum, const void *data, int total_count);
|
||||
int usb_ack(struct usb_device *dev, int epnum);
|
||||
int usb_err(struct usb_device *dev, int epnum);
|
||||
int usb_recv(struct usb_device *dev, void *buffer, unsigned int buffer_len);
|
||||
void usb_poll(struct usb_device *dev);
|
||||
int usb_wait_for_send_done(struct usb_device *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Devan Lai
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software
|
||||
* for any purpose with or without fee is hereby granted, provided
|
||||
* that the above copyright notice and this permission notice
|
||||
* appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WEBUSB_DEFS_H_INCLUDED
|
||||
#define WEBUSB_DEFS_H_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define WEBUSB_REQ_GET_URL 0x02
|
||||
|
||||
#define WEBUSB_DT_URL 3
|
||||
|
||||
#define WEBUSB_URL_SCHEME_HTTP 0
|
||||
#define WEBUSB_URL_SCHEME_HTTPS 1
|
||||
|
||||
struct webusb_platform_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDevCapabilityType;
|
||||
uint8_t bReserved;
|
||||
uint8_t platformCapabilityUUID[16];
|
||||
uint16_t bcdVersion;
|
||||
uint8_t bVendorCode;
|
||||
uint8_t iLandingPage;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define WEBUSB_PLATFORM_DESCRIPTOR_SIZE sizeof(struct webusb_platform_descriptor)
|
||||
|
||||
#define WEBUSB_UUID {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}
|
||||
|
||||
struct webusb_url_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bScheme;
|
||||
char URL[];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define WEBUSB_DT_URL_DESCRIPTOR_SIZE 3
|
||||
|
||||
#endif
|
|
@ -0,0 +1,912 @@
|
|||
/*
|
||||
* xxHash - Fast Hash algorithm
|
||||
* Copyright (C) 2012-2016, Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - xxHash homepage: http://www.xxhash.com
|
||||
* - xxHash source repository : https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Tuning parameters
|
||||
***************************************/
|
||||
/*!XXH_FORCE_MEMORY_ACCESS :
|
||||
* By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
|
||||
* Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
|
||||
* The below switch allow to select different access method for improved performance.
|
||||
* Method 0 (default) : use `memcpy()`. Safe and portable.
|
||||
* Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
|
||||
* This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
|
||||
* Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
|
||||
* It can generate buggy code on targets which do not support unaligned memory accesses.
|
||||
* But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
|
||||
* See http://stackoverflow.com/a/32095106/646947 for details.
|
||||
* Prefer these methods in priority order (0 > 1 > 2)
|
||||
*/
|
||||
#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
|
||||
# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|
||||
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
|
||||
|| defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
|
||||
# define XXH_FORCE_MEMORY_ACCESS 2
|
||||
# elif defined(__INTEL_COMPILER) || \
|
||||
(defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
|
||||
|| defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
|
||||
|| defined(__ARM_ARCH_7S__) ))
|
||||
# define XXH_FORCE_MEMORY_ACCESS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*!XXH_ACCEPT_NULL_INPUT_POINTER :
|
||||
* If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault.
|
||||
* When this macro is enabled, xxHash actively checks input for null pointer.
|
||||
* It it is, result for null input pointers is the same as a null-length input.
|
||||
*/
|
||||
#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */
|
||||
# define XXH_ACCEPT_NULL_INPUT_POINTER 0
|
||||
#endif
|
||||
|
||||
/*!XXH_FORCE_NATIVE_FORMAT :
|
||||
* By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
|
||||
* Results are therefore identical for little-endian and big-endian CPU.
|
||||
* This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
|
||||
* Should endian-independence be of no importance for your application, you may set the #define below to 1,
|
||||
* to improve speed for Big-endian CPU.
|
||||
* This option has no impact on Little_Endian CPU.
|
||||
*/
|
||||
#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */
|
||||
# define XXH_FORCE_NATIVE_FORMAT 0
|
||||
#endif
|
||||
|
||||
/*!XXH_FORCE_ALIGN_CHECK :
|
||||
* This is a minor performance trick, only useful with lots of very small keys.
|
||||
* It means : check for aligned/unaligned input.
|
||||
* The check costs one initial branch per hash;
|
||||
* set it to 0 when the input is guaranteed to be aligned,
|
||||
* or when alignment doesn't matter for performance.
|
||||
*/
|
||||
#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
|
||||
# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
|
||||
# define XXH_FORCE_ALIGN_CHECK 0
|
||||
# else
|
||||
# define XXH_FORCE_ALIGN_CHECK 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Includes & Memory related functions
|
||||
***************************************/
|
||||
/*! Modify the local functions below should you wish to use some other memory routines
|
||||
* for malloc(), free() */
|
||||
#include <stdlib.h>
|
||||
static void* XXH_malloc(size_t s) { return malloc(s); }
|
||||
static void XXH_free (void* p) { free(p); }
|
||||
/*! and for memcpy() */
|
||||
#include <string.h>
|
||||
static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
|
||||
|
||||
#define XXH_STATIC_LINKING_ONLY
|
||||
#include "xxhash.h"
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Compiler Specific Options
|
||||
***************************************/
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
#else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
#endif
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Basic Types
|
||||
***************************************/
|
||||
#ifndef MEM_MODULE
|
||||
# if !defined (__VMS) \
|
||||
&& (defined (__cplusplus) \
|
||||
|| (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
|
||||
# include <stdint.h>
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t U16;
|
||||
typedef uint32_t U32;
|
||||
# else
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short U16;
|
||||
typedef unsigned int U32;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
|
||||
|
||||
/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
|
||||
static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
|
||||
|
||||
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
|
||||
|
||||
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
|
||||
/* currently only defined for gcc and icc */
|
||||
typedef union { U32 u32; } __attribute__((packed)) unalign;
|
||||
static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
|
||||
|
||||
#else
|
||||
|
||||
/* portable and safe solution. Generally efficient.
|
||||
* see : http://stackoverflow.com/a/32095106/646947
|
||||
*/
|
||||
static U32 XXH_read32(const void* memPtr)
|
||||
{
|
||||
U32 val;
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
|
||||
|
||||
|
||||
/* ****************************************
|
||||
* Compiler-specific Functions and Macros
|
||||
******************************************/
|
||||
#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
|
||||
/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
|
||||
#if defined(_MSC_VER)
|
||||
# define XXH_rotl32(x,r) _rotl(x,r)
|
||||
# define XXH_rotl64(x,r) _rotl64(x,r)
|
||||
#else
|
||||
# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
|
||||
# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) /* Visual Studio */
|
||||
# define XXH_swap32 _byteswap_ulong
|
||||
#elif XXH_GCC_VERSION >= 403
|
||||
# define XXH_swap32 __builtin_bswap32
|
||||
#else
|
||||
static U32 XXH_swap32 (U32 x)
|
||||
{
|
||||
return ((x << 24) & 0xff000000 ) |
|
||||
((x << 8) & 0x00ff0000 ) |
|
||||
((x >> 8) & 0x0000ff00 ) |
|
||||
((x >> 24) & 0x000000ff );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Architecture Macros
|
||||
***************************************/
|
||||
typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
|
||||
|
||||
/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
|
||||
#ifndef XXH_CPU_LITTLE_ENDIAN
|
||||
static const int g_one = 1;
|
||||
# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one))
|
||||
#endif
|
||||
|
||||
|
||||
/* ***************************
|
||||
* Memory reads
|
||||
*****************************/
|
||||
typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
|
||||
|
||||
FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
if (align==XXH_unaligned)
|
||||
return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
|
||||
else
|
||||
return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
|
||||
}
|
||||
|
||||
FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
|
||||
{
|
||||
return XXH_readLE32_align(ptr, endian, XXH_unaligned);
|
||||
}
|
||||
|
||||
static U32 XXH_readBE32(const void* ptr)
|
||||
{
|
||||
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
|
||||
}
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Macros
|
||||
***************************************/
|
||||
#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */
|
||||
XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
|
||||
|
||||
|
||||
/* *******************************************************************
|
||||
* 32-bit hash functions
|
||||
*********************************************************************/
|
||||
static const U32 PRIME32_1 = 2654435761U;
|
||||
static const U32 PRIME32_2 = 2246822519U;
|
||||
static const U32 PRIME32_3 = 3266489917U;
|
||||
static const U32 PRIME32_4 = 668265263U;
|
||||
static const U32 PRIME32_5 = 374761393U;
|
||||
|
||||
static U32 XXH32_round(U32 seed, U32 input)
|
||||
{
|
||||
seed += input * PRIME32_2;
|
||||
seed = XXH_rotl32(seed, 13);
|
||||
seed *= PRIME32_1;
|
||||
return seed;
|
||||
}
|
||||
|
||||
FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* bEnd = p + len;
|
||||
U32 h32;
|
||||
#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
|
||||
|
||||
#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
|
||||
if (p==NULL) {
|
||||
len=0;
|
||||
bEnd=p=(const BYTE*)(size_t)16;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len>=16) {
|
||||
const BYTE* const limit = bEnd - 16;
|
||||
U32 v1 = seed + PRIME32_1 + PRIME32_2;
|
||||
U32 v2 = seed + PRIME32_2;
|
||||
U32 v3 = seed + 0;
|
||||
U32 v4 = seed - PRIME32_1;
|
||||
|
||||
do {
|
||||
v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
|
||||
v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
|
||||
v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
|
||||
v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
|
||||
} while (p<=limit);
|
||||
|
||||
h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
|
||||
} else {
|
||||
h32 = seed + PRIME32_5;
|
||||
}
|
||||
|
||||
h32 += (U32) len;
|
||||
|
||||
while (p+4<=bEnd) {
|
||||
h32 += XXH_get32bits(p) * PRIME32_3;
|
||||
h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
|
||||
p+=4;
|
||||
}
|
||||
|
||||
while (p<bEnd) {
|
||||
h32 += (*p) * PRIME32_5;
|
||||
h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
|
||||
p++;
|
||||
}
|
||||
|
||||
h32 ^= h32 >> 15;
|
||||
h32 *= PRIME32_2;
|
||||
h32 ^= h32 >> 13;
|
||||
h32 *= PRIME32_3;
|
||||
h32 ^= h32 >> 16;
|
||||
|
||||
return h32;
|
||||
}
|
||||
|
||||
|
||||
XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
|
||||
{
|
||||
#if 0
|
||||
/* Simple version, good for code maintenance, but unfortunately slow for small inputs */
|
||||
XXH32_state_t state;
|
||||
XXH32_reset(&state, seed);
|
||||
XXH32_update(&state, input, len);
|
||||
return XXH32_digest(&state);
|
||||
#else
|
||||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
|
||||
|
||||
if (XXH_FORCE_ALIGN_CHECK) {
|
||||
if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
|
||||
else
|
||||
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
|
||||
} }
|
||||
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
|
||||
else
|
||||
return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*====== Hash streaming ======*/
|
||||
|
||||
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
|
||||
{
|
||||
return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
|
||||
}
|
||||
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
|
||||
{
|
||||
XXH_free(statePtr);
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
|
||||
{
|
||||
memcpy(dstState, srcState, sizeof(*dstState));
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
|
||||
{
|
||||
XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.v1 = seed + PRIME32_1 + PRIME32_2;
|
||||
state.v2 = seed + PRIME32_2;
|
||||
state.v3 = seed + 0;
|
||||
state.v4 = seed - PRIME32_1;
|
||||
/* do not write into reserved, planned to be removed in a future version */
|
||||
memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE
|
||||
XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* const bEnd = p + len;
|
||||
|
||||
if (input==NULL)
|
||||
#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
|
||||
return XXH_OK;
|
||||
#else
|
||||
return XXH_ERROR;
|
||||
#endif
|
||||
|
||||
state->total_len_32 += (unsigned)len;
|
||||
state->large_len |= (len>=16) | (state->total_len_32>=16);
|
||||
|
||||
if (state->memsize + len < 16) { /* fill in tmp buffer */
|
||||
XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
|
||||
state->memsize += (unsigned)len;
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
if (state->memsize) { /* some data left from previous update */
|
||||
XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
|
||||
{ const unsigned* p32 = state->mem32;
|
||||
state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;
|
||||
state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;
|
||||
state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;
|
||||
state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian));
|
||||
}
|
||||
p += 16-state->memsize;
|
||||
state->memsize = 0;
|
||||
}
|
||||
|
||||
if (p <= bEnd-16) {
|
||||
const BYTE* const limit = bEnd - 16;
|
||||
U32 v1 = state->v1;
|
||||
U32 v2 = state->v2;
|
||||
U32 v3 = state->v3;
|
||||
U32 v4 = state->v4;
|
||||
|
||||
do {
|
||||
v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;
|
||||
v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;
|
||||
v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;
|
||||
v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;
|
||||
} while (p<=limit);
|
||||
|
||||
state->v1 = v1;
|
||||
state->v2 = v2;
|
||||
state->v3 = v3;
|
||||
state->v4 = v4;
|
||||
}
|
||||
|
||||
if (p < bEnd) {
|
||||
XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
|
||||
state->memsize = (unsigned)(bEnd-p);
|
||||
}
|
||||
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
|
||||
{
|
||||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
|
||||
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
|
||||
else
|
||||
return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
|
||||
{
|
||||
const BYTE * p = (const BYTE*)state->mem32;
|
||||
const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;
|
||||
U32 h32;
|
||||
|
||||
if (state->large_len) {
|
||||
h32 = XXH_rotl32(state->v1, 1)
|
||||
+ XXH_rotl32(state->v2, 7)
|
||||
+ XXH_rotl32(state->v3, 12)
|
||||
+ XXH_rotl32(state->v4, 18);
|
||||
} else {
|
||||
h32 = state->v3 /* == seed */ + PRIME32_5;
|
||||
}
|
||||
|
||||
h32 += state->total_len_32;
|
||||
|
||||
while (p+4<=bEnd) {
|
||||
h32 += XXH_readLE32(p, endian) * PRIME32_3;
|
||||
h32 = XXH_rotl32(h32, 17) * PRIME32_4;
|
||||
p+=4;
|
||||
}
|
||||
|
||||
while (p<bEnd) {
|
||||
h32 += (*p) * PRIME32_5;
|
||||
h32 = XXH_rotl32(h32, 11) * PRIME32_1;
|
||||
p++;
|
||||
}
|
||||
|
||||
h32 ^= h32 >> 15;
|
||||
h32 *= PRIME32_2;
|
||||
h32 ^= h32 >> 13;
|
||||
h32 *= PRIME32_3;
|
||||
h32 ^= h32 >> 16;
|
||||
|
||||
return h32;
|
||||
}
|
||||
|
||||
|
||||
XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
|
||||
{
|
||||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
|
||||
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH32_digest_endian(state_in, XXH_littleEndian);
|
||||
else
|
||||
return XXH32_digest_endian(state_in, XXH_bigEndian);
|
||||
}
|
||||
|
||||
|
||||
/*====== Canonical representation ======*/
|
||||
|
||||
/*! Default XXH result types are basic unsigned 32 and 64 bits.
|
||||
* The canonical representation follows human-readable write convention, aka big-endian (large digits first).
|
||||
* These functions allow transformation of hash result into and from its canonical format.
|
||||
* This way, hash values can be written into a file or buffer, remaining comparable across different systems.
|
||||
*/
|
||||
|
||||
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
|
||||
{
|
||||
XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
|
||||
if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
|
||||
memcpy(dst, &hash, sizeof(*dst));
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
|
||||
{
|
||||
return XXH_readBE32(src);
|
||||
}
|
||||
|
||||
|
||||
#ifndef XXH_NO_LONG_LONG
|
||||
|
||||
/* *******************************************************************
|
||||
* 64-bit hash functions
|
||||
*********************************************************************/
|
||||
|
||||
/*====== Memory access ======*/
|
||||
|
||||
#ifndef MEM_MODULE
|
||||
# define MEM_MODULE
|
||||
# if !defined (__VMS) \
|
||||
&& (defined (__cplusplus) \
|
||||
|| (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
|
||||
# include <stdint.h>
|
||||
typedef uint64_t U64;
|
||||
# else
|
||||
/* if compiler doesn't support unsigned long long, replace by another 64-bit type */
|
||||
typedef unsigned long long U64;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
|
||||
|
||||
/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
|
||||
static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
|
||||
|
||||
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
|
||||
|
||||
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
|
||||
/* currently only defined for gcc and icc */
|
||||
typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;
|
||||
static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }
|
||||
|
||||
#else
|
||||
|
||||
/* portable and safe solution. Generally efficient.
|
||||
* see : http://stackoverflow.com/a/32095106/646947
|
||||
*/
|
||||
|
||||
static U64 XXH_read64(const void* memPtr)
|
||||
{
|
||||
U64 val;
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
|
||||
|
||||
#if defined(_MSC_VER) /* Visual Studio */
|
||||
# define XXH_swap64 _byteswap_uint64
|
||||
#elif XXH_GCC_VERSION >= 403
|
||||
# define XXH_swap64 __builtin_bswap64
|
||||
#else
|
||||
static U64 XXH_swap64 (U64 x)
|
||||
{
|
||||
return ((x << 56) & 0xff00000000000000ULL) |
|
||||
((x << 40) & 0x00ff000000000000ULL) |
|
||||
((x << 24) & 0x0000ff0000000000ULL) |
|
||||
((x << 8) & 0x000000ff00000000ULL) |
|
||||
((x >> 8) & 0x00000000ff000000ULL) |
|
||||
((x >> 24) & 0x0000000000ff0000ULL) |
|
||||
((x >> 40) & 0x000000000000ff00ULL) |
|
||||
((x >> 56) & 0x00000000000000ffULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
if (align==XXH_unaligned)
|
||||
return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
|
||||
else
|
||||
return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
|
||||
}
|
||||
|
||||
FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
|
||||
{
|
||||
return XXH_readLE64_align(ptr, endian, XXH_unaligned);
|
||||
}
|
||||
|
||||
static U64 XXH_readBE64(const void* ptr)
|
||||
{
|
||||
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
|
||||
}
|
||||
|
||||
|
||||
/*====== xxh64 ======*/
|
||||
|
||||
static const U64 PRIME64_1 = 11400714785074694791ULL;
|
||||
static const U64 PRIME64_2 = 14029467366897019727ULL;
|
||||
static const U64 PRIME64_3 = 1609587929392839161ULL;
|
||||
static const U64 PRIME64_4 = 9650029242287828579ULL;
|
||||
static const U64 PRIME64_5 = 2870177450012600261ULL;
|
||||
|
||||
static U64 XXH64_round(U64 acc, U64 input)
|
||||
{
|
||||
acc += input * PRIME64_2;
|
||||
acc = XXH_rotl64(acc, 31);
|
||||
acc *= PRIME64_1;
|
||||
return acc;
|
||||
}
|
||||
|
||||
static U64 XXH64_mergeRound(U64 acc, U64 val)
|
||||
{
|
||||
val = XXH64_round(0, val);
|
||||
acc ^= val;
|
||||
acc = acc * PRIME64_1 + PRIME64_4;
|
||||
return acc;
|
||||
}
|
||||
|
||||
FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* bEnd = p + len;
|
||||
U64 h64;
|
||||
#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
|
||||
|
||||
#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
|
||||
if (p==NULL) {
|
||||
len=0;
|
||||
bEnd=p=(const BYTE*)(size_t)32;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len>=32) {
|
||||
const BYTE* const limit = bEnd - 32;
|
||||
U64 v1 = seed + PRIME64_1 + PRIME64_2;
|
||||
U64 v2 = seed + PRIME64_2;
|
||||
U64 v3 = seed + 0;
|
||||
U64 v4 = seed - PRIME64_1;
|
||||
|
||||
do {
|
||||
v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
|
||||
v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
|
||||
v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
|
||||
v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
|
||||
} while (p<=limit);
|
||||
|
||||
h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
|
||||
h64 = XXH64_mergeRound(h64, v1);
|
||||
h64 = XXH64_mergeRound(h64, v2);
|
||||
h64 = XXH64_mergeRound(h64, v3);
|
||||
h64 = XXH64_mergeRound(h64, v4);
|
||||
|
||||
} else {
|
||||
h64 = seed + PRIME64_5;
|
||||
}
|
||||
|
||||
h64 += (U64) len;
|
||||
|
||||
while (p+8<=bEnd) {
|
||||
U64 const k1 = XXH64_round(0, XXH_get64bits(p));
|
||||
h64 ^= k1;
|
||||
h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
|
||||
p+=8;
|
||||
}
|
||||
|
||||
if (p+4<=bEnd) {
|
||||
h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;
|
||||
h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
|
||||
p+=4;
|
||||
}
|
||||
|
||||
while (p<bEnd) {
|
||||
h64 ^= (*p) * PRIME64_5;
|
||||
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
|
||||
p++;
|
||||
}
|
||||
|
||||
h64 ^= h64 >> 33;
|
||||
h64 *= PRIME64_2;
|
||||
h64 ^= h64 >> 29;
|
||||
h64 *= PRIME64_3;
|
||||
h64 ^= h64 >> 32;
|
||||
|
||||
return h64;
|
||||
}
|
||||
|
||||
|
||||
XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
|
||||
{
|
||||
#if 0
|
||||
/* Simple version, good for code maintenance, but unfortunately slow for small inputs */
|
||||
XXH64_state_t state;
|
||||
XXH64_reset(&state, seed);
|
||||
XXH64_update(&state, input, len);
|
||||
return XXH64_digest(&state);
|
||||
#else
|
||||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
|
||||
|
||||
if (XXH_FORCE_ALIGN_CHECK) {
|
||||
if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
|
||||
else
|
||||
return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
|
||||
} }
|
||||
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
|
||||
else
|
||||
return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*====== Hash Streaming ======*/
|
||||
|
||||
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
|
||||
{
|
||||
return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
|
||||
}
|
||||
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
|
||||
{
|
||||
XXH_free(statePtr);
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
|
||||
{
|
||||
memcpy(dstState, srcState, sizeof(*dstState));
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
|
||||
{
|
||||
XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.v1 = seed + PRIME64_1 + PRIME64_2;
|
||||
state.v2 = seed + PRIME64_2;
|
||||
state.v3 = seed + 0;
|
||||
state.v4 = seed - PRIME64_1;
|
||||
/* do not write into reserved, planned to be removed in a future version */
|
||||
memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
FORCE_INLINE
|
||||
XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* const bEnd = p + len;
|
||||
|
||||
if (input==NULL)
|
||||
#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
|
||||
return XXH_OK;
|
||||
#else
|
||||
return XXH_ERROR;
|
||||
#endif
|
||||
|
||||
state->total_len += len;
|
||||
|
||||
if (state->memsize + len < 32) { /* fill in tmp buffer */
|
||||
XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
|
||||
state->memsize += (U32)len;
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
if (state->memsize) { /* tmp buffer is full */
|
||||
XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
|
||||
state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));
|
||||
state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));
|
||||
state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));
|
||||
state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));
|
||||
p += 32-state->memsize;
|
||||
state->memsize = 0;
|
||||
}
|
||||
|
||||
if (p+32 <= bEnd) {
|
||||
const BYTE* const limit = bEnd - 32;
|
||||
U64 v1 = state->v1;
|
||||
U64 v2 = state->v2;
|
||||
U64 v3 = state->v3;
|
||||
U64 v4 = state->v4;
|
||||
|
||||
do {
|
||||
v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;
|
||||
v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;
|
||||
v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;
|
||||
v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;
|
||||
} while (p<=limit);
|
||||
|
||||
state->v1 = v1;
|
||||
state->v2 = v2;
|
||||
state->v3 = v3;
|
||||
state->v4 = v4;
|
||||
}
|
||||
|
||||
if (p < bEnd) {
|
||||
XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
|
||||
state->memsize = (unsigned)(bEnd-p);
|
||||
}
|
||||
|
||||
return XXH_OK;
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
|
||||
{
|
||||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
|
||||
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
|
||||
else
|
||||
return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
|
||||
}
|
||||
|
||||
FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
|
||||
{
|
||||
const BYTE * p = (const BYTE*)state->mem64;
|
||||
const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;
|
||||
U64 h64;
|
||||
|
||||
if (state->total_len >= 32) {
|
||||
U64 const v1 = state->v1;
|
||||
U64 const v2 = state->v2;
|
||||
U64 const v3 = state->v3;
|
||||
U64 const v4 = state->v4;
|
||||
|
||||
h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
|
||||
h64 = XXH64_mergeRound(h64, v1);
|
||||
h64 = XXH64_mergeRound(h64, v2);
|
||||
h64 = XXH64_mergeRound(h64, v3);
|
||||
h64 = XXH64_mergeRound(h64, v4);
|
||||
} else {
|
||||
h64 = state->v3 + PRIME64_5;
|
||||
}
|
||||
|
||||
h64 += (U64) state->total_len;
|
||||
|
||||
while (p+8<=bEnd) {
|
||||
U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian));
|
||||
h64 ^= k1;
|
||||
h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
|
||||
p+=8;
|
||||
}
|
||||
|
||||
if (p+4<=bEnd) {
|
||||
h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;
|
||||
h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
|
||||
p+=4;
|
||||
}
|
||||
|
||||
while (p<bEnd) {
|
||||
h64 ^= (*p) * PRIME64_5;
|
||||
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
|
||||
p++;
|
||||
}
|
||||
|
||||
h64 ^= h64 >> 33;
|
||||
h64 *= PRIME64_2;
|
||||
h64 ^= h64 >> 29;
|
||||
h64 *= PRIME64_3;
|
||||
h64 ^= h64 >> 32;
|
||||
|
||||
return h64;
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)
|
||||
{
|
||||
XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
|
||||
|
||||
if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
|
||||
return XXH64_digest_endian(state_in, XXH_littleEndian);
|
||||
else
|
||||
return XXH64_digest_endian(state_in, XXH_bigEndian);
|
||||
}
|
||||
|
||||
|
||||
/*====== Canonical representation ======*/
|
||||
|
||||
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
|
||||
{
|
||||
XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
|
||||
if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
|
||||
memcpy(dst, &hash, sizeof(*dst));
|
||||
}
|
||||
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
|
||||
{
|
||||
return XXH_readBE64(src);
|
||||
}
|
||||
|
||||
#endif /* XXH_NO_LONG_LONG */
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
xxHash - Extremely Fast Hash algorithm
|
||||
Header File
|
||||
Copyright (C) 2012-2016, Yann Collet.
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at :
|
||||
- xxHash source repository : https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
/* Notice extracted from xxHash homepage :
|
||||
|
||||
xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
|
||||
It also successfully passes all tests from the SMHasher suite.
|
||||
|
||||
Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
|
||||
|
||||
Name Speed Q.Score Author
|
||||
xxHash 5.4 GB/s 10
|
||||
CrapWow 3.2 GB/s 2 Andrew
|
||||
MumurHash 3a 2.7 GB/s 10 Austin Appleby
|
||||
SpookyHash 2.0 GB/s 10 Bob Jenkins
|
||||
SBox 1.4 GB/s 9 Bret Mulvey
|
||||
Lookup3 1.2 GB/s 9 Bob Jenkins
|
||||
SuperFastHash 1.2 GB/s 1 Paul Hsieh
|
||||
CityHash64 1.05 GB/s 10 Pike & Alakuijala
|
||||
FNV 0.55 GB/s 5 Fowler, Noll, Vo
|
||||
CRC32 0.43 GB/s 9
|
||||
MD5-32 0.33 GB/s 10 Ronald L. Rivest
|
||||
SHA1-32 0.28 GB/s 10
|
||||
|
||||
Q.Score is a measure of quality of the hash function.
|
||||
It depends on successfully passing SMHasher test set.
|
||||
10 is a perfect score.
|
||||
|
||||
A 64-bit version, named XXH64, is available since r35.
|
||||
It offers much better speed, but for 64-bit applications only.
|
||||
Name Speed on 64 bits Speed on 32 bits
|
||||
XXH64 13.8 GB/s 1.9 GB/s
|
||||
XXH32 6.8 GB/s 6.0 GB/s
|
||||
*/
|
||||
|
||||
#ifndef XXHASH_H_5627135585666179
|
||||
#define XXHASH_H_5627135585666179 1
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* ****************************
|
||||
* Definitions
|
||||
******************************/
|
||||
#include <stddef.h> /* size_t */
|
||||
typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
|
||||
|
||||
|
||||
/* ****************************
|
||||
* API modifier
|
||||
******************************/
|
||||
/** XXH_PRIVATE_API
|
||||
* This is useful to include xxhash functions in `static` mode
|
||||
* in order to inline them, and remove their symbol from the public list.
|
||||
* Methodology :
|
||||
* #define XXH_PRIVATE_API
|
||||
* #include "xxhash.h"
|
||||
* `xxhash.c` is automatically included.
|
||||
* It's not useful to compile and link it as a separate module.
|
||||
*/
|
||||
#ifdef XXH_PRIVATE_API
|
||||
# ifndef XXH_STATIC_LINKING_ONLY
|
||||
# define XXH_STATIC_LINKING_ONLY
|
||||
# endif
|
||||
# if defined(__GNUC__)
|
||||
# define XXH_PUBLIC_API static __inline __attribute__((unused))
|
||||
# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
|
||||
# define XXH_PUBLIC_API static inline
|
||||
# elif defined(_MSC_VER)
|
||||
# define XXH_PUBLIC_API static __inline
|
||||
# else
|
||||
/* this version may generate warnings for unused static functions */
|
||||
# define XXH_PUBLIC_API static
|
||||
# endif
|
||||
#else
|
||||
# define XXH_PUBLIC_API /* do nothing */
|
||||
#endif /* XXH_PRIVATE_API */
|
||||
|
||||
/*!XXH_NAMESPACE, aka Namespace Emulation :
|
||||
|
||||
If you want to include _and expose_ xxHash functions from within your own library,
|
||||
but also want to avoid symbol collisions with other libraries which may also include xxHash,
|
||||
|
||||
you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
|
||||
with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).
|
||||
|
||||
Note that no change is required within the calling program as long as it includes `xxhash.h` :
|
||||
regular symbol name will be automatically translated by this header.
|
||||
*/
|
||||
#ifdef XXH_NAMESPACE
|
||||
# define XXH_CAT(A,B) A##B
|
||||
# define XXH_NAME2(A,B) XXH_CAT(A,B)
|
||||
# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
|
||||
# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
|
||||
# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
|
||||
# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
|
||||
# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
|
||||
# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
|
||||
# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
|
||||
# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
|
||||
# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
|
||||
# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
|
||||
# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
|
||||
# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
|
||||
# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
|
||||
# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
|
||||
# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
|
||||
# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
|
||||
# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
|
||||
# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
|
||||
# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
|
||||
#endif
|
||||
|
||||
|
||||
/* *************************************
|
||||
* Version
|
||||
***************************************/
|
||||
#define XXH_VERSION_MAJOR 0
|
||||
#define XXH_VERSION_MINOR 6
|
||||
#define XXH_VERSION_RELEASE 4
|
||||
#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
|
||||
XXH_PUBLIC_API unsigned XXH_versionNumber (void);
|
||||
|
||||
|
||||
/*-**********************************************************************
|
||||
* 32-bit hash
|
||||
************************************************************************/
|
||||
typedef unsigned int XXH32_hash_t;
|
||||
|
||||
/*! XXH32() :
|
||||
Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input".
|
||||
The memory between input & input+length must be valid (allocated and read-accessible).
|
||||
"seed" can be used to alter the result predictably.
|
||||
Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */
|
||||
XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
|
||||
|
||||
/*====== Streaming ======*/
|
||||
typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */
|
||||
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
|
||||
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
|
||||
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
|
||||
|
||||
XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed);
|
||||
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
|
||||
XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
|
||||
|
||||
/*
|
||||
These functions generate the xxHash of an input provided in multiple segments.
|
||||
Note that, for small input, they are slower than single-call functions, due to state management.
|
||||
For small input, prefer `XXH32()` and `XXH64()` .
|
||||
|
||||
XXH state must first be allocated, using XXH*_createState() .
|
||||
|
||||
Start a new hash by initializing state with a seed, using XXH*_reset().
|
||||
|
||||
Then, feed the hash state by calling XXH*_update() as many times as necessary.
|
||||
Obviously, input must be allocated and read accessible.
|
||||
The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
|
||||
|
||||
Finally, a hash value can be produced anytime, by using XXH*_digest().
|
||||
This function returns the nn-bits hash as an int or long long.
|
||||
|
||||
It's still possible to continue inserting input into the hash state after a digest,
|
||||
and generate some new hashes later on, by calling again XXH*_digest().
|
||||
|
||||
When done, free XXH state space if it was allocated dynamically.
|
||||
*/
|
||||
|
||||
/*====== Canonical representation ======*/
|
||||
|
||||
typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
|
||||
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
|
||||
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
|
||||
|
||||
/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
|
||||
* The canonical representation uses human-readable write convention, aka big-endian (large digits first).
|
||||
* These functions allow transformation of hash result into and from its canonical format.
|
||||
* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef XXH_NO_LONG_LONG
|
||||
/*-**********************************************************************
|
||||
* 64-bit hash
|
||||
************************************************************************/
|
||||
typedef unsigned long long XXH64_hash_t;
|
||||
|
||||
/*! XXH64() :
|
||||
Calculate the 64-bit hash of sequence of length "len" stored at memory address "input".
|
||||
"seed" can be used to alter the result predictably.
|
||||
This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark).
|
||||
*/
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
|
||||
|
||||
/*====== Streaming ======*/
|
||||
typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */
|
||||
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
|
||||
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
|
||||
XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
|
||||
|
||||
XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);
|
||||
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
|
||||
|
||||
/*====== Canonical representation ======*/
|
||||
typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
|
||||
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
|
||||
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
|
||||
#endif /* XXH_NO_LONG_LONG */
|
||||
|
||||
|
||||
#ifdef XXH_STATIC_LINKING_ONLY
|
||||
|
||||
/* ================================================================================================
|
||||
This section contains declarations which are not guaranteed to remain stable.
|
||||
They may change in future versions, becoming incompatible with a different version of the library.
|
||||
These declarations should only be used with static linking.
|
||||
Never use them in association with dynamic linking !
|
||||
=================================================================================================== */
|
||||
|
||||
/* These definitions are only meant to make possible
|
||||
static allocation of XXH state, on stack or in a struct for example.
|
||||
Never use members directly. */
|
||||
|
||||
struct XXH32_state_s {
|
||||
unsigned total_len_32;
|
||||
unsigned large_len;
|
||||
unsigned v1;
|
||||
unsigned v2;
|
||||
unsigned v3;
|
||||
unsigned v4;
|
||||
unsigned mem32[4]; /* buffer defined as U32 for alignment */
|
||||
unsigned memsize;
|
||||
unsigned reserved; /* never read nor write, will be removed in a future version */
|
||||
}; /* typedef'd to XXH32_state_t */
|
||||
|
||||
#ifndef XXH_NO_LONG_LONG /* remove 64-bit support */
|
||||
struct XXH64_state_s {
|
||||
unsigned long long total_len;
|
||||
unsigned long long v1;
|
||||
unsigned long long v2;
|
||||
unsigned long long v3;
|
||||
unsigned long long v4;
|
||||
unsigned long long mem64[4]; /* buffer defined as U64 for alignment */
|
||||
unsigned memsize;
|
||||
unsigned reserved[2]; /* never read nor write, will be removed in a future version */
|
||||
}; /* typedef'd to XXH64_state_t */
|
||||
#endif
|
||||
|
||||
#ifdef XXH_PRIVATE_API
|
||||
# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */
|
||||
#endif
|
||||
|
||||
#endif /* XXH_STATIC_LINKING_ONLY */
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* XXHASH_H_5627135585666179 */
|
|
@ -1,15 +1,16 @@
|
|||
INCLUDE ld/output_format.ld
|
||||
INCLUDE output_format.ld
|
||||
ENTRY(_start)
|
||||
|
||||
__DYNAMIC = 0;
|
||||
|
||||
INCLUDE ld/regions.ld
|
||||
INCLUDE regions.ld
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_ftext = .;
|
||||
*(.text.start)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
_etext = .;
|
||||
} > rom
|
||||
|
@ -31,7 +32,7 @@ SECTIONS
|
|||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
*(.data1)
|
||||
_gp = ALIGN(16);
|
||||
*(.sdata .sdata.* .gnu.linkonce.s.*)
|
||||
*(.sdata .sdata.* .gnu.linkonce.s.* .sdata2 .sdata2.*)
|
||||
_edata = ALIGN(16); /* Make sure _edata is >= _gp. */
|
||||
} > sram
|
||||
|
|
@ -1 +1 @@
|
|||
OUTPUT_FORMAT("elf32-littleriscv")
|
||||
OUTPUT_FORMAT("elf32-littleriscv")
|
|
@ -1,5 +1,4 @@
|
|||
MEMORY {
|
||||
spiflash : ORIGIN = 0x20000000, LENGTH = 0x00200000
|
||||
sram : ORIGIN = 0x10000000, LENGTH = 0x00020000
|
||||
rom : ORIGIN = 0x00000000, LENGTH = 0x00002000
|
||||
}
|
||||
MEMORY {
|
||||
sram : ORIGIN = 0x10000000, LENGTH = 0x00020000
|
||||
rom : ORIGIN = 0x00000000, LENGTH = 0x00002000
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Fadecandy DFU Bootloader
|
||||
*
|
||||
* Copyright (c) 2013 Micah Elizabeth Scott
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <toboot-api.h>
|
||||
#include <toboot-internal.h>
|
||||
#include <dfu.h>
|
||||
#include <rgb.h>
|
||||
|
||||
#define RESCUE_IMAGE_OFFSET 262144
|
||||
|
||||
#define ERASE_SIZE 65536 // Erase block size (in bytes)
|
||||
#define WRITE_SIZE 256 // Number of bytes we can write
|
||||
|
||||
#include <spi.h>
|
||||
extern struct ff_spi *spi; // Defined in main.c
|
||||
|
||||
// Internal flash-programming state machine
|
||||
static unsigned fl_current_addr = 0;
|
||||
static enum {
|
||||
flsIDLE = 0,
|
||||
flsERASING,
|
||||
flsPROGRAMMING
|
||||
} fl_state;
|
||||
|
||||
static dfu_state_t dfu_state = dfuIDLE;
|
||||
static dfu_status_t dfu_status = OK;
|
||||
static unsigned dfu_poll_timeout_ms = 1;
|
||||
|
||||
static uint32_t dfu_buffer[DFU_TRANSFER_SIZE/4];
|
||||
static uint32_t dfu_buffer_offset;
|
||||
static uint32_t dfu_bytes_remaining;
|
||||
|
||||
// Memory offset we're uploading to.
|
||||
static uint32_t dfu_target_address;
|
||||
|
||||
static void set_state(dfu_state_t new_state, dfu_status_t new_status) {
|
||||
if (new_state == dfuIDLE)
|
||||
rgb_mode_idle();
|
||||
else if (new_status != OK)
|
||||
rgb_mode_error();
|
||||
else if (new_state == dfuMANIFEST_WAIT_RESET)
|
||||
rgb_mode_done();
|
||||
else
|
||||
rgb_mode_writing();
|
||||
dfu_state = new_state;
|
||||
dfu_status = new_status;
|
||||
}
|
||||
|
||||
static bool ftfl_busy()
|
||||
{
|
||||
// Is the flash memory controller busy?
|
||||
return spiIsBusy(spi);
|
||||
}
|
||||
|
||||
static void ftfl_busy_wait()
|
||||
{
|
||||
// Wait for the flash memory controller to finish any pending operation.
|
||||
while (ftfl_busy())
|
||||
;//watchdog_refresh();
|
||||
}
|
||||
|
||||
static void ftfl_begin_erase_sector(uint32_t address)
|
||||
{
|
||||
ftfl_busy_wait();
|
||||
// Only erase if it's on the page boundry.
|
||||
if ((address & ~(ERASE_SIZE - 1) ) == address)
|
||||
spiBeginErase64(spi, address);
|
||||
fl_state = flsERASING;
|
||||
}
|
||||
|
||||
static void ftfl_write_more_bytes(void)
|
||||
{
|
||||
uint32_t bytes_to_write = WRITE_SIZE;
|
||||
if (dfu_bytes_remaining < bytes_to_write)
|
||||
bytes_to_write = dfu_bytes_remaining;
|
||||
ftfl_busy_wait();
|
||||
spiBeginWrite(spi, dfu_target_address, &dfu_buffer[dfu_buffer_offset], bytes_to_write);
|
||||
|
||||
dfu_bytes_remaining -= bytes_to_write;
|
||||
dfu_target_address += bytes_to_write;
|
||||
dfu_buffer_offset += bytes_to_write;
|
||||
}
|
||||
|
||||
static void ftfl_begin_program_section(uint32_t address)
|
||||
{
|
||||
// Write the buffer word to the currently selected address.
|
||||
// Note that after this is done, the address is incremented by 4.
|
||||
dfu_buffer_offset = 0;
|
||||
dfu_target_address = address;
|
||||
ftfl_write_more_bytes();
|
||||
}
|
||||
|
||||
static uint32_t address_for_block(unsigned blockNum)
|
||||
{
|
||||
static const uint32_t starting_offset = RESCUE_IMAGE_OFFSET;
|
||||
return starting_offset + (blockNum * WRITE_SIZE);
|
||||
}
|
||||
|
||||
void dfu_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t dfu_getstate(void)
|
||||
{
|
||||
return dfu_state;
|
||||
}
|
||||
|
||||
bool dfu_download(unsigned blockNum, unsigned blockLength,
|
||||
unsigned packetOffset, unsigned packetLength, const uint8_t *data)
|
||||
{
|
||||
if (packetOffset + packetLength > DFU_TRANSFER_SIZE ||
|
||||
packetOffset + packetLength > blockLength) {
|
||||
|
||||
// Overflow!
|
||||
set_state(dfuERROR, errADDRESS);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store more data...
|
||||
memcpy(((uint8_t *)dfu_buffer) + packetOffset, data, packetLength);
|
||||
|
||||
if (packetOffset + packetLength != blockLength) {
|
||||
// Still waiting for more data.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dfu_state != dfuIDLE && dfu_state != dfuDNLOAD_IDLE) {
|
||||
// Wrong state! Oops.
|
||||
set_state(dfuERROR, errSTALLEDPKT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ftfl_busy() || (fl_state != flsIDLE)) {
|
||||
// Flash controller shouldn't be busy now!
|
||||
set_state(dfuERROR, errWRITE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!blockLength) {
|
||||
// End of download
|
||||
set_state(dfuMANIFEST_SYNC, OK);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Start programming a block by erasing the corresponding flash sector
|
||||
fl_state = flsERASING;
|
||||
fl_current_addr = address_for_block(blockNum);
|
||||
dfu_bytes_remaining = blockLength;
|
||||
|
||||
ftfl_begin_erase_sector(fl_current_addr);
|
||||
|
||||
set_state(dfuDNLOAD_SYNC, OK);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void fl_state_poll(void)
|
||||
{
|
||||
// Try to advance the state of our own flash programming state machine.
|
||||
|
||||
if (spiIsBusy(spi))
|
||||
return;
|
||||
|
||||
switch (fl_state) {
|
||||
|
||||
case flsIDLE:
|
||||
break;
|
||||
|
||||
case flsERASING:
|
||||
fl_state = flsPROGRAMMING;
|
||||
ftfl_begin_program_section(fl_current_addr);
|
||||
break;
|
||||
|
||||
case flsPROGRAMMING:
|
||||
// Program more blocks, if applicable
|
||||
if (dfu_bytes_remaining)
|
||||
ftfl_write_more_bytes();
|
||||
else
|
||||
fl_state = flsIDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dfu_poll(void)
|
||||
{
|
||||
if ((dfu_state == dfuDNLOAD_SYNC) || (dfu_state == dfuDNBUSY))
|
||||
fl_state_poll();
|
||||
}
|
||||
|
||||
bool dfu_getstatus(uint8_t status[8])
|
||||
{
|
||||
switch (dfu_state) {
|
||||
|
||||
case dfuDNLOAD_SYNC:
|
||||
case dfuDNBUSY:
|
||||
|
||||
if (dfu_state == dfuERROR) {
|
||||
// An error occurred inside fl_state_poll();
|
||||
} else if (fl_state == flsIDLE) {
|
||||
set_state(dfuDNLOAD_IDLE, OK);
|
||||
} else {
|
||||
set_state(dfuDNBUSY, OK);
|
||||
}
|
||||
break;
|
||||
|
||||
case dfuMANIFEST_SYNC:
|
||||
// Ready to reboot. The main thread will take care of this. Also let the DFU tool
|
||||
// know to leave us alone until this happens.
|
||||
set_state(dfuMANIFEST, OK);
|
||||
dfu_poll_timeout_ms = 10;
|
||||
break;
|
||||
|
||||
case dfuMANIFEST:
|
||||
// Perform the reboot
|
||||
set_state(dfuMANIFEST_WAIT_RESET, OK);
|
||||
dfu_poll_timeout_ms = 1000;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
status[0] = dfu_status;
|
||||
status[1] = dfu_poll_timeout_ms;
|
||||
status[2] = dfu_poll_timeout_ms >> 8;
|
||||
status[3] = dfu_poll_timeout_ms >> 16;
|
||||
status[4] = dfu_state;
|
||||
status[5] = 0; // iString
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dfu_clrstatus(void)
|
||||
{
|
||||
switch (dfu_state) {
|
||||
|
||||
case dfuERROR:
|
||||
case dfuIDLE:
|
||||
case dfuMANIFEST_WAIT_RESET:
|
||||
// Clear an error
|
||||
set_state(dfuIDLE, OK);
|
||||
return true;
|
||||
|
||||
default:
|
||||
// Unexpected request
|
||||
set_state(dfuERROR, errSTALLEDPKT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool dfu_abort(void)
|
||||
{
|
||||
set_state(dfuIDLE, OK);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
#include <stdio.h>
|
||||
#include <irq.h>
|
||||
#include <printf.h>
|
||||
#include <uart.h>
|
||||
#include <usb.h>
|
||||
#include <time.h>
|
||||
#include <dfu.h>
|
||||
#include <rgb.h>
|
||||
#include <spi.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
struct ff_spi *spi;
|
||||
|
||||
void isr(void)
|
||||
{
|
||||
unsigned int irqs;
|
||||
|
||||
irqs = irq_pending() & irq_getmask();
|
||||
|
||||
if (irqs & (1 << USB_INTERRUPT))
|
||||
usb_isr();
|
||||
|
||||
#ifdef CSR_UART_BASE
|
||||
if (irqs & (1 << UART_INTERRUPT))
|
||||
uart_isr();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CSR_UART_BASE
|
||||
static void rv_putchar(void *ignored, char c)
|
||||
{
|
||||
(void)ignored;
|
||||
if (c == '\n')
|
||||
uart_write('\r');
|
||||
if (c == '\r')
|
||||
return;
|
||||
uart_write(c);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
#ifdef CSR_UART_BASE
|
||||
init_printf(NULL, rv_putchar);
|
||||
#endif
|
||||
irq_setmask(0);
|
||||
irq_setie(1);
|
||||
uart_init();
|
||||
usb_init();
|
||||
dfu_init();
|
||||
time_init();
|
||||
rgb_init();
|
||||
|
||||
spi = spiAlloc();
|
||||
spiSetPin(spi, SP_MOSI, 0);
|
||||
spiSetPin(spi, SP_MISO, 1);
|
||||
spiSetPin(spi, SP_WP, 2);
|
||||
spiSetPin(spi, SP_HOLD, 3);
|
||||
spiSetPin(spi, SP_CLK, 4);
|
||||
spiSetPin(spi, SP_CS, 5);
|
||||
spiSetPin(spi, SP_D0, 0);
|
||||
spiSetPin(spi, SP_D1, 1);
|
||||
spiSetPin(spi, SP_D2, 2);
|
||||
spiSetPin(spi, SP_D3, 3);
|
||||
spiInit(spi);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char *usb_hw_api(void) {
|
||||
#ifdef CSR_USB_EP_0_OUT_EV_PENDING_ADDR
|
||||
return "epfifo";
|
||||
#else
|
||||
#ifdef CSR_USB_OBUF_EMPTY_ADDR
|
||||
return "rawfifo";
|
||||
#else
|
||||
#ifdef CSR_USB_WHATEVER
|
||||
return "whatever";
|
||||
#else
|
||||
return "unrecognized hw api";
|
||||
#endif /* CSR_USB_WHATEVER */
|
||||
#endif /* CSR_USB_OBUF_EMPTY_ADDR */
|
||||
#endif /* CSR_USB_EP_0_OUT_EV_PENDING_ADDR */
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
init();
|
||||
|
||||
usb_connect();
|
||||
while (1)
|
||||
{
|
||||
usb_poll(NULL);
|
||||
dfu_poll();
|
||||
// if (i > 200)
|
||||
// reboot_ctrl_write(0xac);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
#include <rgb.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
enum led_registers {
|
||||
LEDDCR0 = 8,
|
||||
LEDDBR = 9,
|
||||
LEDDONR = 10,
|
||||
LEDDOFR = 11,
|
||||
LEDDBCRR = 5,
|
||||
LEDDBCFR = 6,
|
||||
LEDDPWRR = 1,
|
||||
LEDDPWRG = 2,
|
||||
LEDDPWRB = 3,
|
||||
};
|
||||
|
||||
#define BREATHE_ENABLE (1 << 7)
|
||||
#define BREATHE_EDGE_ON (0 << 6)
|
||||
#define BREATHE_EDGE_BOTH (1 << 6)
|
||||
#define BREATHE_MODE_MODULATE (1 << 5)
|
||||
#define BREATHE_RATE(x) ((x & 7) << 0)
|
||||
|
||||
#define RGB_SWITCH_MODE(x) do { \
|
||||
if (rgb_mode == x) \
|
||||
return; \
|
||||
rgb_mode = x; \
|
||||
/* Toggle LEDD_EXE to force the mode to switch */ \
|
||||
rgb_ctrl_write( (1 << 1) | (1 << 2)); \
|
||||
rgb_ctrl_write((1 << 0) | (1 << 1) | (1 << 2)); \
|
||||
} while(0)
|
||||
|
||||
static enum {
|
||||
INVALID = 0,
|
||||
IDLE,
|
||||
WRITING,
|
||||
ERROR,
|
||||
DONE,
|
||||
} rgb_mode;
|
||||
|
||||
static void rgb_write(uint8_t value, uint8_t addr) {
|
||||
rgb_addr_write(addr);
|
||||
rgb_dat_write(value);
|
||||
}
|
||||
|
||||
void rgb_init(void) {
|
||||
// Turn on the RGB block and current enable, as well as enabling led control
|
||||
rgb_ctrl_write((1 << 0) | (1 << 1) | (1 << 2));
|
||||
|
||||
// Enable the LED driver, and set 250 Hz mode.
|
||||
// Also set quick stop, which we'll use to switch patterns quickly.
|
||||
rgb_write((1 << 7) | (1 << 6) | (1 << 3), LEDDCR0);
|
||||
|
||||
// Set clock register to 12 MHz / 64 kHz - 1
|
||||
rgb_write((12000000/64000)-1, LEDDBR);
|
||||
|
||||
rgb_mode_idle();
|
||||
}
|
||||
|
||||
void rgb_mode_idle(void) {
|
||||
RGB_SWITCH_MODE(IDLE);
|
||||
// rgb_mode_writing(); return;
|
||||
rgb_write(12, LEDDONR);
|
||||
rgb_write(24, LEDDOFR);
|
||||
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_EDGE_BOTH
|
||||
| BREATHE_MODE_MODULATE | BREATHE_RATE(2), LEDDBCRR);
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_MODE_MODULATE | BREATHE_RATE(3), LEDDBCFR);
|
||||
|
||||
rgb_write(0x00/4, LEDDPWRG); // Red
|
||||
rgb_write(0x4a/4, LEDDPWRB); // Green
|
||||
rgb_write(0xe1/4, LEDDPWRR); // Blue
|
||||
}
|
||||
|
||||
void rgb_mode_writing(void) {
|
||||
RGB_SWITCH_MODE(WRITING);
|
||||
rgb_write(1, LEDDONR);
|
||||
rgb_write(2, LEDDOFR);
|
||||
|
||||
rgb_write(BREATHE_ENABLE | 0
|
||||
| BREATHE_MODE_MODULATE | BREATHE_RATE(1), LEDDBCRR);
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_MODE_MODULATE | BREATHE_RATE(3), LEDDBCFR);
|
||||
|
||||
rgb_write(0x00/4, LEDDPWRG); // Red
|
||||
rgb_write(0x7a/4, LEDDPWRB); // Green
|
||||
rgb_write(0x51/4, LEDDPWRR); // Blue
|
||||
}
|
||||
|
||||
void rgb_mode_error(void) {
|
||||
RGB_SWITCH_MODE(ERROR);
|
||||
rgb_write(3, LEDDONR);
|
||||
rgb_write(3, LEDDOFR);
|
||||
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_EDGE_BOTH
|
||||
| BREATHE_MODE_MODULATE | BREATHE_RATE(2), LEDDBCRR);
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_MODE_MODULATE | BREATHE_RATE(3), LEDDBCFR);
|
||||
|
||||
rgb_write(0xf0/4, LEDDPWRG); // Red
|
||||
rgb_write(0x0a/4, LEDDPWRB); // Green
|
||||
rgb_write(0x01/4, LEDDPWRR); // Blue
|
||||
}
|
||||
|
||||
void rgb_mode_done(void) {
|
||||
RGB_SWITCH_MODE(DONE);
|
||||
rgb_write(8, LEDDONR);
|
||||
rgb_write(8, LEDDOFR);
|
||||
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_EDGE_BOTH
|
||||
| BREATHE_MODE_MODULATE | BREATHE_RATE(2), LEDDBCRR);
|
||||
rgb_write(BREATHE_ENABLE | BREATHE_MODE_MODULATE | BREATHE_RATE(3), LEDDBCFR);
|
||||
|
||||
rgb_write(0x14/4, LEDDPWRG); // Red
|
||||
rgb_write(0xff/4, LEDDPWRB); // Green
|
||||
rgb_write(0x44/4, LEDDPWRR); // Blue
|
||||
}
|
|
@ -0,0 +1,948 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <printf.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include "spi.h"
|
||||
|
||||
#define fprintf(...) do {} while(0)
|
||||
|
||||
static uint8_t do_mirror;
|
||||
static uint8_t oe_mirror;
|
||||
|
||||
#define PI_OUTPUT 1
|
||||
#define PI_INPUT 0
|
||||
#define PI_ALT0 PI_INPUT
|
||||
static void gpioSetMode(int pin, int mode) {
|
||||
if (mode)
|
||||
oe_mirror |= 1 << pin;
|
||||
else
|
||||
oe_mirror &= ~(1 << pin);
|
||||
bbspi_oe_write(oe_mirror);
|
||||
}
|
||||
|
||||
static void gpioWrite(int pin, int val) {
|
||||
if (val)
|
||||
do_mirror |= 1 << pin;
|
||||
else
|
||||
do_mirror &= ~(1 << pin);
|
||||
bbspi_do_write(do_mirror);
|
||||
}
|
||||
|
||||
static int gpioRead(int pin) {
|
||||
return !!(bbspi_di_read() & (1 << pin));
|
||||
}
|
||||
|
||||
static void gpioSync(void) {
|
||||
// bbspi_do_write(do_mirror);
|
||||
}
|
||||
|
||||
#define SPI_ONLY_SINGLE
|
||||
|
||||
enum ff_spi_quirks {
|
||||
// There is no separate "Write SR 2" command. Instead,
|
||||
// you must write SR2 after writing SR1
|
||||
SQ_SR2_FROM_SR1 = (1 << 0),
|
||||
|
||||
// Don't issue a "Write Enable" command prior to writing
|
||||
// a status register
|
||||
SQ_SKIP_SR_WEL = (1 << 1),
|
||||
|
||||
// Security registers are shifted up by 4 bits
|
||||
SQ_SECURITY_NYBBLE_SHIFT = (1 << 2),
|
||||
};
|
||||
|
||||
struct ff_spi {
|
||||
enum spi_state state;
|
||||
enum spi_type type;
|
||||
enum spi_type desired_type;
|
||||
struct spi_id id;
|
||||
enum ff_spi_quirks quirks;
|
||||
int size_override;
|
||||
|
||||
struct {
|
||||
int clk;
|
||||
int d0;
|
||||
int d1;
|
||||
int d2;
|
||||
int d3;
|
||||
int wp;
|
||||
int hold;
|
||||
int cs;
|
||||
int miso;
|
||||
int mosi;
|
||||
} pins;
|
||||
};
|
||||
|
||||
static void spi_get_id(struct ff_spi *spi);
|
||||
|
||||
static void spi_set_state(struct ff_spi *spi, enum spi_state state) {
|
||||
return;
|
||||
if (spi->state == state)
|
||||
return;
|
||||
#ifndef SPI_ONLY_SINGLE
|
||||
switch (state) {
|
||||
case SS_SINGLE:
|
||||
#endif
|
||||
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_OUTPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_OUTPUT);
|
||||
#ifndef SPI_ONLY_SINGLE
|
||||
break;
|
||||
|
||||
case SS_DUAL_RX:
|
||||
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_INPUT); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_OUTPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_OUTPUT);
|
||||
break;
|
||||
|
||||
case SS_DUAL_TX:
|
||||
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_OUTPUT); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_OUTPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_OUTPUT);
|
||||
break;
|
||||
|
||||
case SS_QUAD_RX:
|
||||
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_INPUT); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_INPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_INPUT);
|
||||
break;
|
||||
|
||||
case SS_QUAD_TX:
|
||||
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_OUTPUT); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_OUTPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_OUTPUT);
|
||||
break;
|
||||
|
||||
case SS_HARDWARE:
|
||||
gpioSetMode(spi->pins.clk, PI_ALT0); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_ALT0); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_ALT0); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_ALT0); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_OUTPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_OUTPUT);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unrecognized spi state\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
spi->state = state;
|
||||
}
|
||||
|
||||
void spiPause(struct ff_spi *spi) {
|
||||
(void)spi;
|
||||
gpioSync();
|
||||
// usleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
void spiBegin(struct ff_spi *spi) {
|
||||
spi_set_state(spi, SS_SINGLE);
|
||||
if ((spi->type == ST_SINGLE) || (spi->type == ST_DUAL)) {
|
||||
gpioWrite(spi->pins.wp, 1);
|
||||
gpioWrite(spi->pins.hold, 1);
|
||||
}
|
||||
gpioWrite(spi->pins.cs, 0);
|
||||
}
|
||||
|
||||
void spiEnd(struct ff_spi *spi) {
|
||||
(void)spi;
|
||||
gpioWrite(spi->pins.cs, 1);
|
||||
}
|
||||
|
||||
static uint8_t spiXfer(struct ff_spi *spi, uint8_t out) {
|
||||
int bit;
|
||||
uint8_t in = 0;
|
||||
|
||||
for (bit = 7; bit >= 0; bit--) {
|
||||
if (out & (1 << bit)) {
|
||||
gpioWrite(spi->pins.mosi, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.mosi, 0);
|
||||
}
|
||||
gpioWrite(spi->pins.clk, 1);
|
||||
spiPause(spi);
|
||||
in |= ((!!gpioRead(spi->pins.miso)) << bit);
|
||||
gpioWrite(spi->pins.clk, 0);
|
||||
spiPause(spi);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
static void spiSingleTx(struct ff_spi *spi, uint8_t out) {
|
||||
spi_set_state(spi, SS_SINGLE);
|
||||
spiXfer(spi, out);
|
||||
}
|
||||
|
||||
static uint8_t spiSingleRx(struct ff_spi *spi) {
|
||||
spi_set_state(spi, SS_SINGLE);
|
||||
return spiXfer(spi, 0xff);
|
||||
}
|
||||
|
||||
static void spiDualTx(struct ff_spi *spi, uint8_t out) {
|
||||
|
||||
int bit;
|
||||
spi_set_state(spi, SS_DUAL_TX);
|
||||
for (bit = 7; bit >= 0; bit -= 2) {
|
||||
if (out & (1 << (bit - 1))) {
|
||||
gpioWrite(spi->pins.d0, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.d0, 0);
|
||||
}
|
||||
|
||||
if (out & (1 << (bit - 0))) {
|
||||
gpioWrite(spi->pins.d1, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.d1, 0);
|
||||
}
|
||||
gpioWrite(spi->pins.clk, 1);
|
||||
spiPause(spi);
|
||||
gpioWrite(spi->pins.clk, 0);
|
||||
spiPause(spi);
|
||||
}
|
||||
}
|
||||
|
||||
static void spiQuadTx(struct ff_spi *spi, uint8_t out) {
|
||||
int bit;
|
||||
spi_set_state(spi, SS_QUAD_TX);
|
||||
for (bit = 7; bit >= 0; bit -= 4) {
|
||||
if (out & (1 << (bit - 3))) {
|
||||
gpioWrite(spi->pins.d0, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.d0, 0);
|
||||
}
|
||||
|
||||
if (out & (1 << (bit - 2))) {
|
||||
gpioWrite(spi->pins.d1, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.d1, 0);
|
||||
}
|
||||
|
||||
if (out & (1 << (bit - 1))) {
|
||||
gpioWrite(spi->pins.d2, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.d2, 0);
|
||||
}
|
||||
|
||||
if (out & (1 << (bit - 0))) {
|
||||
gpioWrite(spi->pins.d3, 1);
|
||||
}
|
||||
else {
|
||||
gpioWrite(spi->pins.d3, 0);
|
||||
}
|
||||
gpioWrite(spi->pins.clk, 1);
|
||||
spiPause(spi);
|
||||
gpioWrite(spi->pins.clk, 0);
|
||||
spiPause(spi);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t spiDualRx(struct ff_spi *spi) {
|
||||
int bit;
|
||||
uint8_t in = 0;
|
||||
|
||||
spi_set_state(spi, SS_QUAD_RX);
|
||||
for (bit = 7; bit >= 0; bit -= 2) {
|
||||
gpioWrite(spi->pins.clk, 1);
|
||||
spiPause(spi);
|
||||
in |= ((!!gpioRead(spi->pins.d0)) << (bit - 1));
|
||||
in |= ((!!gpioRead(spi->pins.d1)) << (bit - 0));
|
||||
gpioWrite(spi->pins.clk, 0);
|
||||
spiPause(spi);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
static uint8_t spiQuadRx(struct ff_spi *spi) {
|
||||
int bit;
|
||||
uint8_t in = 0;
|
||||
|
||||
spi_set_state(spi, SS_QUAD_RX);
|
||||
for (bit = 7; bit >= 0; bit -= 4) {
|
||||
gpioWrite(spi->pins.clk, 1);
|
||||
spiPause(spi);
|
||||
in |= ((!!gpioRead(spi->pins.d0)) << (bit - 3));
|
||||
in |= ((!!gpioRead(spi->pins.d1)) << (bit - 2));
|
||||
in |= ((!!gpioRead(spi->pins.d2)) << (bit - 1));
|
||||
in |= ((!!gpioRead(spi->pins.d3)) << (bit - 0));
|
||||
gpioWrite(spi->pins.clk, 0);
|
||||
spiPause(spi);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
int spiTx(struct ff_spi *spi, uint8_t word) {
|
||||
switch (spi->type) {
|
||||
case ST_SINGLE:
|
||||
spiSingleTx(spi, word);
|
||||
break;
|
||||
case ST_DUAL:
|
||||
spiDualTx(spi, word);
|
||||
break;
|
||||
case ST_QUAD:
|
||||
case ST_QPI:
|
||||
spiQuadTx(spi, word);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t spiRx(struct ff_spi *spi) {
|
||||
switch (spi->type) {
|
||||
case ST_SINGLE:
|
||||
return spiSingleRx(spi);
|
||||
case ST_DUAL:
|
||||
return spiDualRx(spi);
|
||||
case ST_QUAD:
|
||||
case ST_QPI:
|
||||
return spiQuadRx(spi);
|
||||
default:
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void spiCommand(struct ff_spi *spi, uint8_t cmd) {
|
||||
if (spi->type == ST_QPI)
|
||||
spiQuadTx(spi, cmd);
|
||||
else
|
||||
spiSingleTx(spi, cmd);
|
||||
}
|
||||
|
||||
uint8_t spiCommandRx(struct ff_spi *spi) {
|
||||
if (spi->type == ST_QPI)
|
||||
return spiQuadRx(spi);
|
||||
else
|
||||
return spiSingleRx(spi);
|
||||
}
|
||||
|
||||
uint8_t spiReadStatus(struct ff_spi *spi, uint8_t sr) {
|
||||
uint8_t val = 0xff;
|
||||
(void)sr;
|
||||
|
||||
#if 0
|
||||
switch (sr) {
|
||||
case 1:
|
||||
#endif
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x05);
|
||||
val = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
#if 0
|
||||
break;
|
||||
|
||||
case 2:
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x35);
|
||||
val = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x15);
|
||||
val = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "unrecognized status register: %d\n", sr);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
void spiWriteSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]) {
|
||||
|
||||
if (spi->quirks & SQ_SECURITY_NYBBLE_SHIFT)
|
||||
sr <<= 4;
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
// erase the register
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x44);
|
||||
spiCommand(spi, 0x00); // A23-16
|
||||
spiCommand(spi, sr); // A15-8
|
||||
spiCommand(spi, 0x00); // A0-7
|
||||
spiEnd(spi);
|
||||
|
||||
spi_get_id(spi);
|
||||
sleep(1);
|
||||
|
||||
// write enable
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x42);
|
||||
spiCommand(spi, 0x00); // A23-16
|
||||
spiCommand(spi, sr); // A15-8
|
||||
spiCommand(spi, 0x00); // A0-7
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
spiCommand(spi, security[i]);
|
||||
spiEnd(spi);
|
||||
|
||||
spi_get_id(spi);
|
||||
}
|
||||
|
||||
void spiReadSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]) {
|
||||
if (spi->quirks & SQ_SECURITY_NYBBLE_SHIFT)
|
||||
sr <<= 4;
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x48); // Read security registers
|
||||
spiCommand(spi, 0x00); // A23-16
|
||||
spiCommand(spi, sr); // A15-8
|
||||
spiCommand(spi, 0x00); // A0-7
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
security[i] = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
}
|
||||
|
||||
void spiWriteStatus(struct ff_spi *spi, uint8_t sr, uint8_t val) {
|
||||
|
||||
switch (sr) {
|
||||
case 1:
|
||||
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
}
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x50);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x01);
|
||||
spiCommand(spi, val);
|
||||
spiEnd(spi);
|
||||
break;
|
||||
|
||||
case 2: {
|
||||
uint8_t sr1 = 0x00;
|
||||
if (spi->quirks & SQ_SR2_FROM_SR1)
|
||||
sr1 = spiReadStatus(spi, 1);
|
||||
|
||||
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
}
|
||||
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x50);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
if (spi->quirks & SQ_SR2_FROM_SR1) {
|
||||
spiCommand(spi, 0x01);
|
||||
spiCommand(spi, sr1);
|
||||
spiCommand(spi, val);
|
||||
}
|
||||
else {
|
||||
spiCommand(spi, 0x31);
|
||||
spiCommand(spi, val);
|
||||
}
|
||||
spiEnd(spi);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
}
|
||||
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x50);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x11);
|
||||
spiCommand(spi, val);
|
||||
spiEnd(spi);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "unrecognized status register: %d\n", sr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct spi_id spiId(struct ff_spi *spi) {
|
||||
return spi->id;
|
||||
}
|
||||
|
||||
static void spi_decode_id(struct ff_spi *spi) {
|
||||
spi->id.bytes = -1; // unknown
|
||||
|
||||
if (spi->id.manufacturer_id == 0xef) {
|
||||
// spi->id.manufacturer = "Winbond";
|
||||
if ((spi->id.memory_type == 0x70)
|
||||
&& (spi->id.memory_size == 0x18)) {
|
||||
// spi->id.model = "W25Q128JV";
|
||||
// spi->id.capacity = "128 Mbit";
|
||||
spi->id.bytes = 16 * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
if (spi->id.manufacturer_id == 0x1f) {
|
||||
// spi->id.manufacturer = "Adesto";
|
||||
if ((spi->id.memory_type == 0x86)
|
||||
&& (spi->id.memory_size == 0x01)) {
|
||||
// spi->id.model = "AT25SF161";
|
||||
// spi->id.capacity = "16 Mbit";
|
||||
spi->id.bytes = 1 * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void spi_get_id(struct ff_spi *spi) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x90); // Read manufacturer ID
|
||||
spiCommand(spi, 0x00); // Dummy byte 1
|
||||
spiCommand(spi, 0x00); // Dummy byte 2
|
||||
spiCommand(spi, 0x00); // Dummy byte 3
|
||||
spi->id.manufacturer_id = spiCommandRx(spi);
|
||||
spi->id.device_id = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x9f); // Read device id
|
||||
spi->id._manufacturer_id = spiCommandRx(spi);
|
||||
spi->id.memory_type = spiCommandRx(spi);
|
||||
spi->id.memory_size = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xab); // Read electronic signature
|
||||
spiCommand(spi, 0x00); // Dummy byte 1
|
||||
spiCommand(spi, 0x00); // Dummy byte 2
|
||||
spiCommand(spi, 0x00); // Dummy byte 3
|
||||
spi->id.signature = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x4b); // Read unique ID
|
||||
spiCommand(spi, 0x00); // Dummy byte 1
|
||||
spiCommand(spi, 0x00); // Dummy byte 2
|
||||
spiCommand(spi, 0x00); // Dummy byte 3
|
||||
spiCommand(spi, 0x00); // Dummy byte 4
|
||||
spi->id.serial[0] = spiCommandRx(spi);
|
||||
spi->id.serial[1] = spiCommandRx(spi);
|
||||
spi->id.serial[2] = spiCommandRx(spi);
|
||||
spi->id.serial[3] = spiCommandRx(spi);
|
||||
spiEnd(spi);
|
||||
|
||||
spi_decode_id(spi);
|
||||
return;
|
||||
}
|
||||
|
||||
void spiOverrideSize(struct ff_spi *spi, uint32_t size) {
|
||||
spi->size_override = size;
|
||||
|
||||
// If size is 0, re-read the capacity
|
||||
if (!size)
|
||||
spi_decode_id(spi);
|
||||
else
|
||||
spi->id.bytes = size;
|
||||
}
|
||||
|
||||
int spiSetType(struct ff_spi *spi, enum spi_type type) {
|
||||
|
||||
if (spi->type == type)
|
||||
return 0;
|
||||
|
||||
#ifndef SPI_ONLY_SINGLE
|
||||
switch (type) {
|
||||
|
||||
case ST_SINGLE:
|
||||
#endif
|
||||
if (spi->type == ST_QPI) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xff); // Exit QPI Mode
|
||||
spiEnd(spi);
|
||||
}
|
||||
spi->type = type;
|
||||
spi_set_state(spi, SS_SINGLE);
|
||||
#ifndef SPI_ONLY_SINGLE
|
||||
break;
|
||||
|
||||
case ST_DUAL:
|
||||
if (spi->type == ST_QPI) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xff); // Exit QPI Mode
|
||||
spiEnd(spi);
|
||||
}
|
||||
spi->type = type;
|
||||
spi_set_state(spi, SS_DUAL_TX);
|
||||
break;
|
||||
|
||||
case ST_QUAD:
|
||||
if (spi->type == ST_QPI) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xff); // Exit QPI Mode
|
||||
spiEnd(spi);
|
||||
}
|
||||
|
||||
// Enable QE bit
|
||||
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
|
||||
|
||||
spi->type = type;
|
||||
spi_set_state(spi, SS_QUAD_TX);
|
||||
break;
|
||||
|
||||
case ST_QPI:
|
||||
// Enable QE bit
|
||||
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x38); // Enter QPI Mode
|
||||
spiEnd(spi);
|
||||
spi->type = type;
|
||||
spi_set_state(spi, SS_QUAD_TX);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unrecognized SPI type: %d\n", type);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spiRead(struct ff_spi *spi, uint32_t addr, uint8_t *data, unsigned int count) {
|
||||
|
||||
unsigned int i;
|
||||
|
||||
spiBegin(spi);
|
||||
switch (spi->type) {
|
||||
case ST_SINGLE:
|
||||
case ST_QPI:
|
||||
spiCommand(spi, 0x0b);
|
||||
break;
|
||||
case ST_DUAL:
|
||||
spiCommand(spi, 0x3b);
|
||||
break;
|
||||
case ST_QUAD:
|
||||
spiCommand(spi, 0x6b);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unrecognized spi mode\n");
|
||||
spiEnd(spi);
|
||||
return 1;
|
||||
}
|
||||
spiCommand(spi, addr >> 16);
|
||||
spiCommand(spi, addr >> 8);
|
||||
spiCommand(spi, addr >> 0);
|
||||
spiCommand(spi, 0x00);
|
||||
for (i = 0; i < count; i++) {
|
||||
if ((i & 0x3fff) == 0) {
|
||||
// printf("\rReading @ %06x / %06x", i, count);
|
||||
fflush(stdout);
|
||||
}
|
||||
data[i] = spiRx(spi);
|
||||
}
|
||||
// printf("\rReading @ %06x / %06x Done\n", i, count);
|
||||
|
||||
spiEnd(spi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_wait_for_not_busy(struct ff_spi *spi) {
|
||||
uint8_t sr1;
|
||||
sr1 = spiReadStatus(spi, 1);
|
||||
|
||||
do {
|
||||
sr1 = spiReadStatus(spi, 1);
|
||||
} while (sr1 & (1 << 0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spiIsBusy(struct ff_spi *spi) {
|
||||
return spiReadStatus(spi, 1) & (1 << 0);
|
||||
}
|
||||
|
||||
int spiBeginErase32(struct ff_spi *spi, uint32_t erase_addr) {
|
||||
// Enable Write-Enable Latch (WEL)
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x52);
|
||||
spiCommand(spi, erase_addr >> 16);
|
||||
spiCommand(spi, erase_addr >> 8);
|
||||
spiCommand(spi, erase_addr >> 0);
|
||||
spiEnd(spi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spiBeginErase64(struct ff_spi *spi, uint32_t erase_addr) {
|
||||
// Enable Write-Enable Latch (WEL)
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xD8);
|
||||
spiCommand(spi, erase_addr >> 16);
|
||||
spiCommand(spi, erase_addr >> 8);
|
||||
spiCommand(spi, erase_addr >> 0);
|
||||
spiEnd(spi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spiBeginWrite(struct ff_spi *spi, uint32_t addr, const void *v_data, unsigned int count) {
|
||||
uint8_t write_cmd = 0x02;
|
||||
const uint8_t *data = v_data;
|
||||
unsigned int i;
|
||||
|
||||
// Enable Write-Enable Latch (WEL)
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
// uint8_t sr1 = spiReadStatus(spi, 1);
|
||||
// if (!(sr1 & (1 << 1)))
|
||||
// fprintf(stderr, "error: write-enable latch (WEL) not set, write will probably fail\n");
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, write_cmd);
|
||||
spiCommand(spi, addr >> 16);
|
||||
spiCommand(spi, addr >> 8);
|
||||
spiCommand(spi, addr >> 0);
|
||||
for (i = 0; (i < count) && (i < 256); i++)
|
||||
spiTx(spi, *data++);
|
||||
spiEnd(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spiSwapTxRx(struct ff_spi *spi) {
|
||||
int tmp = spi->pins.mosi;
|
||||
spi->pins.mosi = spi->pins.miso;
|
||||
spi->pins.miso = tmp;
|
||||
spiSetType(spi, ST_SINGLE);
|
||||
spi->state = SS_UNCONFIGURED;
|
||||
spi_set_state(spi, SS_SINGLE);
|
||||
}
|
||||
|
||||
int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned int count) {
|
||||
|
||||
unsigned int i;
|
||||
|
||||
if (addr & 0xff) {
|
||||
fprintf(stderr, "Error: Target address is not page-aligned to 256 bytes\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Erase all applicable blocks
|
||||
uint32_t erase_addr;
|
||||
for (erase_addr = 0; erase_addr < count; erase_addr += 32768) {
|
||||
// printf("\rErasing @ %06x / %06x", erase_addr, count);
|
||||
fflush(stdout);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x52);
|
||||
spiCommand(spi, erase_addr >> 16);
|
||||
spiCommand(spi, erase_addr >> 8);
|
||||
spiCommand(spi, erase_addr >> 0);
|
||||
spiEnd(spi);
|
||||
|
||||
spi_wait_for_not_busy(spi);
|
||||
}
|
||||
printf(" Done\n");
|
||||
|
||||
uint8_t write_cmd;
|
||||
switch (spi->type) {
|
||||
case ST_DUAL:
|
||||
fprintf(stderr, "dual writes are broken -- need to temporarily set SINGLE mode\n");
|
||||
return 1;
|
||||
case ST_SINGLE:
|
||||
case ST_QPI:
|
||||
write_cmd = 0x02;
|
||||
break;
|
||||
case ST_QUAD:
|
||||
write_cmd = 0x32;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unrecognized spi mode\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
fflush(stdout);
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x06);
|
||||
spiEnd(spi);
|
||||
|
||||
uint8_t sr1 = spiReadStatus(spi, 1);
|
||||
if (!(sr1 & (1 << 1)))
|
||||
fprintf(stderr, "error: write-enable latch (WEL) not set, write will probably fail\n");
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, write_cmd);
|
||||
spiCommand(spi, addr >> 16);
|
||||
spiCommand(spi, addr >> 8);
|
||||
spiCommand(spi, addr >> 0);
|
||||
for (i = 0; (i < count) && (i < 256); i++)
|
||||
spiTx(spi, *data++);
|
||||
spiEnd(spi);
|
||||
count -= i;
|
||||
addr += i;
|
||||
spi_wait_for_not_busy(spi);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t spiReset(struct ff_spi *spi) {
|
||||
// XXX You should check the "Ready" bit before doing this!
|
||||
|
||||
// Shift to QPI mode, then back to Single mode, to ensure
|
||||
// we're actually in Single mode.
|
||||
spiSetType(spi, ST_QPI);
|
||||
spiSetType(spi, ST_SINGLE);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x66); // "Enable Reset" command
|
||||
spiEnd(spi);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0x99); // "Reset Device" command
|
||||
spiEnd(spi);
|
||||
|
||||
#pragma warn "Sleep for 30 ms here"
|
||||
// usleep(30);
|
||||
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xab); // "Resume from Deep Power-Down" command
|
||||
spiEnd(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spiInit(struct ff_spi *spi) {
|
||||
spi->state = SS_UNCONFIGURED;
|
||||
spi->type = ST_UNCONFIGURED;
|
||||
|
||||
// Reset the SPI flash, which will return it to SPI mode even
|
||||
// if it's in QPI mode.
|
||||
spiReset(spi);
|
||||
|
||||
spiSetType(spi, ST_SINGLE);
|
||||
|
||||
// Have the SPI flash pay attention to us
|
||||
gpioWrite(spi->pins.hold, 1);
|
||||
|
||||
// Disable WP
|
||||
gpioWrite(spi->pins.wp, 1);
|
||||
|
||||
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
|
||||
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
|
||||
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
|
||||
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
|
||||
gpioSetMode(spi->pins.hold, PI_OUTPUT);
|
||||
gpioSetMode(spi->pins.wp, PI_OUTPUT);
|
||||
|
||||
spi_get_id(spi);
|
||||
|
||||
spi->quirks |= SQ_SR2_FROM_SR1;
|
||||
// if (spi->id.manufacturer_id == 0x1f)
|
||||
if (spi->id.manufacturer_id == 0xef)
|
||||
spi->quirks |= SQ_SKIP_SR_WEL | SQ_SECURITY_NYBBLE_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ff_spi *spiAlloc(void) {
|
||||
static struct ff_spi spi;
|
||||
// struct ff_spi *spi = (struct ff_spi *)malloc(sizeof(struct ff_spi));
|
||||
// memset(&spi, 0, sizeof(spi));
|
||||
return &spi;
|
||||
}
|
||||
|
||||
void spiSetPin(struct ff_spi *spi, enum spi_pin pin, int val) {
|
||||
switch (pin) {
|
||||
case SP_MOSI: spi->pins.mosi = val; break;
|
||||
case SP_MISO: spi->pins.miso = val; break;
|
||||
case SP_HOLD: spi->pins.hold = val; break;
|
||||
case SP_WP: spi->pins.wp = val; break;
|
||||
case SP_CS: spi->pins.cs = val; break;
|
||||
case SP_CLK: spi->pins.clk = val; break;
|
||||
case SP_D0: spi->pins.d0 = val; break;
|
||||
case SP_D1: spi->pins.d1 = val; break;
|
||||
case SP_D2: spi->pins.d2 = val; break;
|
||||
case SP_D3: spi->pins.d3 = val; break;
|
||||
default: fprintf(stderr, "unrecognized pin: %d\n", pin); break;
|
||||
}
|
||||
}
|
||||
|
||||
void spiHold(struct ff_spi *spi) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xb9);
|
||||
spiEnd(spi);
|
||||
}
|
||||
void spiUnhold(struct ff_spi *spi) {
|
||||
spiBegin(spi);
|
||||
spiCommand(spi, 0xab);
|
||||
spiEnd(spi);
|
||||
}
|
||||
|
||||
void spiFree(struct ff_spi **spi) {
|
||||
if (!spi)
|
||||
return;
|
||||
if (!*spi)
|
||||
return;
|
||||
|
||||
spi_set_state(*spi, SS_HARDWARE);
|
||||
// free(*spi);
|
||||
// *spi = NULL;
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
#include <unistd.h>
|
||||
#include "toboot-api.h"
|
||||
#include "toboot-internal.h"
|
||||
|
||||
// #define XXH_NO_LONG_LONG
|
||||
// #define XXH_FORCE_ALIGN_CHECK 0
|
||||
// #define XXH_FORCE_NATIVE_FORMAT 0
|
||||
// #define XXH_PRIVATE_API
|
||||
// #include "xxhash.h"
|
||||
|
||||
static const struct toboot_configuration *current_config = NULL;
|
||||
|
||||
uint32_t tb_first_free_address(void) {
|
||||
return 131072;
|
||||
/*
|
||||
extern uint32_t _eflash;
|
||||
extern uint32_t _sdtext;
|
||||
extern uint32_t _edtext;
|
||||
#define PADDR(x) ((uint32_t)&x)
|
||||
#define PAGE_SIZE 1024
|
||||
#define PAGE_ROUND_UP(x) ( (((uint32_t)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) )
|
||||
return PAGE_ROUND_UP(PADDR(_eflash) + (PADDR(_edtext) - PADDR(_sdtext)));
|
||||
#undef PADDR
|
||||
#undef PAGE_SIZE
|
||||
#undef PAGE_ROUND_UP
|
||||
*/
|
||||
}
|
||||
|
||||
uint32_t tb_config_hash(const struct toboot_configuration *cfg) {
|
||||
return 0;//XXH32(cfg, sizeof(*cfg) - 4, TOBOOT_HASH_SEED);
|
||||
}
|
||||
|
||||
void tb_sign_config(struct toboot_configuration *cfg) {
|
||||
cfg->reserved_hash = tb_config_hash(cfg);
|
||||
}
|
||||
|
||||
int tb_valid_signature_at_page(uint32_t page) {
|
||||
const struct toboot_configuration *cfg = (const struct toboot_configuration *)((page * 1024) + 0x94);
|
||||
if (cfg->magic != TOBOOT_V2_MAGIC)
|
||||
return -1;
|
||||
|
||||
uint32_t calc_hash = tb_config_hash(cfg);
|
||||
if (calc_hash != cfg->reserved_hash)
|
||||
return -2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t tb_first_free_sector(void) {
|
||||
return tb_first_free_address() / 1024;
|
||||
}
|
||||
|
||||
const struct toboot_configuration *tb_get_config(void) {
|
||||
uint32_t __app_start__ = 131072;
|
||||
|
||||
// When examining every application in flash, find the newest program
|
||||
// with the highest generation counter.
|
||||
uint32_t newest_generation = 0;
|
||||
|
||||
// Fake toboot config, for v1 and v0 programs.
|
||||
static struct toboot_configuration fake_config;
|
||||
|
||||
if (current_config)
|
||||
return current_config;
|
||||
|
||||
// Look for a V2 header
|
||||
uint32_t page;
|
||||
for (page = 1; page < 65536/1024; page++) {
|
||||
if (!tb_valid_signature_at_page(page)) {
|
||||
const struct toboot_configuration *test_cfg = (const struct toboot_configuration *)((page * 1024) + 0x94);
|
||||
if (test_cfg->reserved_gen > newest_generation) {
|
||||
newest_generation = test_cfg->reserved_gen;
|
||||
current_config = test_cfg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_config)
|
||||
return current_config;
|
||||
|
||||
// No V2 header found, so create one.
|
||||
|
||||
// Fake V2 magic
|
||||
fake_config.magic = TOBOOT_V2_MAGIC;
|
||||
|
||||
if (((*((uint32_t *)(((uint32_t)&__app_start__) + 0x98))) & TOBOOT_V1_MAGIC_MASK) == TOBOOT_V1_MAGIC)
|
||||
// Applications that know about Toboot will indicate their block
|
||||
// offset by placing a magic byte at offset 0x98.
|
||||
// Ordinarily this would be the address offset for IRQ 22,
|
||||
// but since there are only 20 IRQs on the EFM32HG, there are three
|
||||
// 32-bit values that are unused starting at offset 0x94.
|
||||
// We already use offset 0x94 for "disable boot", so use offset 0x98
|
||||
// in the incoming stream to indicate flags for Toboot.
|
||||
fake_config.start = ((*((uint32_t *)(((uint32_t)&__app_start__) + 0x98))) & TOBOOT_V1_APP_PAGE_MASK) >> TOBOOT_V1_APP_PAGE_SHIFT;
|
||||
else
|
||||
// Default to offset 0x4000
|
||||
fake_config.start = 16;
|
||||
|
||||
// Leave interrupts enabled (and indicate the header is fake)
|
||||
fake_config.config = TOBOOT_CONFIG_FLAG_ENABLE_IRQ | TOBOOT_CONFIG_FAKE;
|
||||
|
||||
// Lock out bootloader entry, if the magic value is present
|
||||
if (((*((uint32_t *)(((uint32_t)&__app_start__) + 0x94))) & TOBOOT_V1_CFG_MAGIC_MASK) == TOBOOT_V1_CFG_MAGIC)
|
||||
fake_config.lock_entry = TOBOOT_LOCKOUT_MAGIC;
|
||||
else
|
||||
fake_config.lock_entry = 0;
|
||||
|
||||
// Don't erase anything in particular
|
||||
fake_config.erase_mask_lo = 0;
|
||||
fake_config.erase_mask_hi = 0;
|
||||
|
||||
// Calculate a valid hash
|
||||
tb_sign_config(&fake_config);
|
||||
|
||||
return &fake_config;
|
||||
}
|
||||
|
||||
uint32_t tb_generation(const struct toboot_configuration *cfg) {
|
||||
if (!cfg)
|
||||
return 0;
|
||||
return cfg->reserved_gen;
|
||||
}
|
||||
/*
|
||||
__attribute__ ((used, section(".toboot_configuration"))) struct toboot_configuration toboot_configuration = {
|
||||
.magic = TOBOOT_V2_MAGIC,
|
||||
|
||||
// The current "generation" flag sits at the same location as the
|
||||
// old Toboot "Config" flag. By setting "reserved_gen" to this value,
|
||||
// we can make the V1 bootloader treat V2 images as valid.
|
||||
.reserved_gen = TOBOOT_V1_APP_MAGIC,
|
||||
|
||||
.start = 0,
|
||||
.config = 0,
|
||||
|
||||
.lock_entry = 0,
|
||||
.erase_mask_lo = 0,
|
||||
.erase_mask_hi = 0,
|
||||
.reserved_hash = 0,
|
||||
};
|
||||
*/
|
|
@ -0,0 +1,229 @@
|
|||
/* Teensyduino Core Library
|
||||
* http://www.pjrc.com/teensy/
|
||||
* Copyright (c) 2013 PJRC.COM, LLC.
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* 1. The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 2. If the Software is incorporated into a build system that allows
|
||||
* selection among a list of target devices, then similar target
|
||||
* devices manufactured by PJRC.COM must be included in the list of
|
||||
* target devices and selectable in the same manner.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <usb-desc.h>
|
||||
|
||||
// USB Descriptors are binary data which the USB host reads to
|
||||
// automatically detect a USB device's capabilities. The format
|
||||
// and meaning of every field is documented in numerous USB
|
||||
// standards. When working with USB descriptors, despite the
|
||||
// complexity of the standards and poor writing quality in many
|
||||
// of those documents, remember descriptors are nothing more
|
||||
// than constant binary data that tells the USB host what the
|
||||
// device can do. Computers will load drivers based on this data.
|
||||
// Those drivers then communicate on the endpoints specified by
|
||||
// the descriptors.
|
||||
|
||||
// To configure a new combination of interfaces or make minor
|
||||
// changes to existing configuration (eg, change the name or ID
|
||||
// numbers), usually you would edit "usb_desc.h". This file
|
||||
// is meant to be configured by the header, so generally it is
|
||||
// only edited to add completely new USB interfaces or features.
|
||||
|
||||
// **************************************************************
|
||||
// USB Device
|
||||
// **************************************************************
|
||||
|
||||
#define LSB(n) ((n) & 255)
|
||||
#define MSB(n) (((n) >> 8) & 255)
|
||||
|
||||
#define USB_DT_BOS_SIZE 5
|
||||
#define USB_DT_BOS 0xf
|
||||
#define USB_DT_DEVICE_CAPABILITY 0x10
|
||||
#define USB_DC_PLATFORM 5
|
||||
|
||||
struct usb_bos_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bNumDeviceCaps;
|
||||
} __attribute__((packed));
|
||||
|
||||
// USB Device Descriptor. The USB host reads this first, to learn
|
||||
// what type of device is connected.
|
||||
static const uint8_t device_descriptor[] = {
|
||||
18, // bLength
|
||||
1, // bDescriptorType
|
||||
0x10, 0x02, // bcdUSB
|
||||
0x00, // bDeviceClass
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
EP0_SIZE, // bMaxPacketSize0
|
||||
LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor
|
||||
LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct
|
||||
LSB(DEVICE_VER), MSB(DEVICE_VER), // bcdDevice
|
||||
1, // iManufacturer
|
||||
2, // iProduct
|
||||
0, // iSerialNumber
|
||||
1 // bNumConfigurations
|
||||
};
|
||||
|
||||
// These descriptors must NOT be "const", because the USB DMA
|
||||
// has trouble accessing flash memory with enough bandwidth
|
||||
// while the processor is executing from flash.
|
||||
|
||||
|
||||
// **************************************************************
|
||||
// USB Configuration
|
||||
// **************************************************************
|
||||
|
||||
// USB Configuration Descriptor. This huge descriptor tells all
|
||||
// of the devices capbilities.
|
||||
static const uint8_t config_descriptor[CONFIG_DESC_SIZE] = {
|
||||
// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
|
||||
9, // bLength;
|
||||
2, // bDescriptorType;
|
||||
LSB(CONFIG_DESC_SIZE), // wTotalLength
|
||||
MSB(CONFIG_DESC_SIZE),
|
||||
NUM_INTERFACE, // bNumInterfaces
|
||||
1, // bConfigurationValue
|
||||
1, // iConfiguration
|
||||
0x80, // bmAttributes
|
||||
50, // bMaxPower
|
||||
|
||||
// interface descriptor, DFU Mode (DFU spec Table 4.4)
|
||||
9, // bLength
|
||||
4, // bDescriptorType
|
||||
DFU_INTERFACE, // bInterfaceNumber
|
||||
0, // bAlternateSetting
|
||||
0, // bNumEndpoints
|
||||
0xFE, // bInterfaceClass
|
||||
0x01, // bInterfaceSubClass
|
||||
0x02, // bInterfaceProtocol
|
||||
2, // iInterface
|
||||
|
||||
// DFU Functional Descriptor (DFU spec Table 4.2)
|
||||
9, // bLength
|
||||
0x21, // bDescriptorType
|
||||
0x0D, // bmAttributes
|
||||
LSB(DFU_DETACH_TIMEOUT), // wDetachTimeOut
|
||||
MSB(DFU_DETACH_TIMEOUT),
|
||||
LSB(DFU_TRANSFER_SIZE), // wTransferSize
|
||||
MSB(DFU_TRANSFER_SIZE),
|
||||
0x01,0x01, // bcdDFUVersion
|
||||
};
|
||||
|
||||
|
||||
// **************************************************************
|
||||
// String Descriptors
|
||||
// **************************************************************
|
||||
|
||||
// The descriptors above can provide human readable strings,
|
||||
// referenced by index numbers. These descriptors are the
|
||||
// actual string data
|
||||
|
||||
static const struct usb_string_descriptor_struct string0 = {
|
||||
4,
|
||||
3,
|
||||
{0x0409}
|
||||
};
|
||||
|
||||
// Microsoft OS String Descriptor. See: https://github.com/pbatard/libwdi/wiki/WCID-Devices
|
||||
static const struct usb_string_descriptor_struct usb_string_microsoft = {
|
||||
18, 3,
|
||||
{'M','S','F','T','1','0','0', MSFT_VENDOR_CODE}
|
||||
};
|
||||
|
||||
// Microsoft WCID
|
||||
const uint8_t usb_microsoft_wcid[MSFT_WCID_LEN] = {
|
||||
MSFT_WCID_LEN, 0, 0, 0, // Length
|
||||
0x00, 0x01, // Version
|
||||
0x04, 0x00, // Compatibility ID descriptor index
|
||||
0x01, // Number of sections
|
||||
0, 0, 0, 0, 0, 0, 0, // Reserved (7 bytes)
|
||||
|
||||
0, // Interface number
|
||||
0x01, // Reserved
|
||||
'W','I','N','U','S','B',0,0, // Compatible ID
|
||||
0,0,0,0,0,0,0,0, // Sub-compatible ID (unused)
|
||||
0,0,0,0,0,0, // Reserved
|
||||
};
|
||||
|
||||
const struct webusb_url_descriptor landing_url_descriptor = {
|
||||
.bLength = LANDING_PAGE_DESCRIPTOR_SIZE,
|
||||
.bDescriptorType = WEBUSB_DT_URL,
|
||||
.bScheme = WEBUSB_URL_SCHEME_HTTPS,
|
||||
.URL = LANDING_PAGE_URL
|
||||
};
|
||||
|
||||
struct full_bos {
|
||||
struct usb_bos_descriptor bos;
|
||||
struct webusb_platform_descriptor webusb;
|
||||
};
|
||||
|
||||
static const struct full_bos full_bos = {
|
||||
.bos = {
|
||||
.bLength = USB_DT_BOS_SIZE,
|
||||
.bDescriptorType = USB_DT_BOS,
|
||||
.wTotalLength = USB_DT_BOS_SIZE + WEBUSB_PLATFORM_DESCRIPTOR_SIZE,
|
||||
.bNumDeviceCaps = 1,
|
||||
},
|
||||
.webusb = {
|
||||
.bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE_CAPABILITY,
|
||||
.bDevCapabilityType = USB_DC_PLATFORM,
|
||||
.bReserved = 0,
|
||||
.platformCapabilityUUID = WEBUSB_UUID,
|
||||
.bcdVersion = 0x0100,
|
||||
.bVendorCode = WEBUSB_VENDOR_CODE,
|
||||
.iLandingPage = 1,
|
||||
},
|
||||
};
|
||||
|
||||
__attribute__((aligned(4)))
|
||||
static const struct usb_string_descriptor_struct usb_string_manufacturer_name = {
|
||||
2 + MANUFACTURER_NAME_LEN,
|
||||
3,
|
||||
MANUFACTURER_NAME
|
||||
};
|
||||
|
||||
__attribute__((aligned(4)))
|
||||
struct usb_string_descriptor_struct usb_string_product_name = {
|
||||
2 + PRODUCT_NAME_LEN,
|
||||
3,
|
||||
PRODUCT_NAME
|
||||
};
|
||||
|
||||
// **************************************************************
|
||||
// Descriptors List
|
||||
// **************************************************************
|
||||
|
||||
// This table provides access to all the descriptor data above.
|
||||
|
||||
const usb_descriptor_list_t usb_descriptor_list[] = {
|
||||
{0x0100, sizeof(device_descriptor), device_descriptor},
|
||||
{0x0200, sizeof(config_descriptor), config_descriptor},
|
||||
{0x0300, 0, (const uint8_t *)&string0},
|
||||
{0x0301, 0, (const uint8_t *)&usb_string_manufacturer_name},
|
||||
{0x0302, 0, (const uint8_t *)&usb_string_product_name},
|
||||
{0x03EE, 0, (const uint8_t *)&usb_string_microsoft},
|
||||
{0x0F00, sizeof(full_bos), (const uint8_t *)&full_bos},
|
||||
{0, 0, NULL}
|
||||
};
|
|
@ -0,0 +1,271 @@
|
|||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <usb.h>
|
||||
#include <dfu.h>
|
||||
#include <system.h>
|
||||
#include <printf.h>
|
||||
|
||||
#include <usb-desc.h>
|
||||
|
||||
static uint8_t reply_buffer[8];
|
||||
static uint8_t usb_configuration = 0;
|
||||
#define USB_MAX_PACKET_SIZE 64
|
||||
static uint32_t rx_buffer[USB_MAX_PACKET_SIZE/4];
|
||||
|
||||
void usb_setup(struct usb_device *dev, const struct usb_setup_request *setup)
|
||||
{
|
||||
const uint8_t *data = NULL;
|
||||
uint32_t datalen = 0;
|
||||
const usb_descriptor_list_t *list;
|
||||
|
||||
switch (setup->wRequestAndType)
|
||||
{
|
||||
case 0x0500: // SET_ADDRESS
|
||||
case 0x0b01: // SET_INTERFACE
|
||||
dfu_clrstatus();
|
||||
break;
|
||||
|
||||
case 0x0900: // SET_CONFIGURATION
|
||||
usb_configuration = setup->wValue;
|
||||
break;
|
||||
|
||||
case 0x0880: // GET_CONFIGURATION
|
||||
reply_buffer[0] = usb_configuration;
|
||||
datalen = 1;
|
||||
data = reply_buffer;
|
||||
break;
|
||||
|
||||
case 0x0080: // GET_STATUS (device)
|
||||
reply_buffer[0] = 0;
|
||||
reply_buffer[1] = 0;
|
||||
datalen = 2;
|
||||
data = reply_buffer;
|
||||
break;
|
||||
|
||||
case 0x0082: // GET_STATUS (endpoint)
|
||||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
reply_buffer[0] = 0;
|
||||
reply_buffer[1] = 0;
|
||||
|
||||
// XXX handle endpoint stall here
|
||||
// if (USB->DIEP0CTL & USB_DIEP_CTL_STALL)
|
||||
// reply_buffer[0] = 1;
|
||||
data = reply_buffer;
|
||||
datalen = 2;
|
||||
break;
|
||||
|
||||
case 0x0102: // CLEAR_FEATURE (endpoint)
|
||||
if (setup->wIndex > 0 || setup->wValue != 0)
|
||||
{
|
||||
// TODO: do we need to handle IN vs OUT here?
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
// XXX: Should we clear the stall bit?
|
||||
// USB->DIEP0CTL &= ~USB_DIEP_CTL_STALL;
|
||||
// TODO: do we need to clear the data toggle here?
|
||||
break;
|
||||
|
||||
case 0x0302: // SET_FEATURE (endpoint)
|
||||
if (setup->wIndex > 0 || setup->wValue != 0)
|
||||
{
|
||||
// TODO: do we need to handle IN vs OUT here?
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
// XXX: Should we set the stall bit?
|
||||
// USB->DIEP0CTL |= USB_DIEP_CTL_STALL;
|
||||
// TODO: do we need to clear the data toggle here?
|
||||
break;
|
||||
|
||||
case 0x0680: // GET_DESCRIPTOR
|
||||
case 0x0681:
|
||||
for (list = usb_descriptor_list; 1; list++)
|
||||
{
|
||||
if (list->addr == NULL)
|
||||
break;
|
||||
if (setup->wValue == list->wValue)
|
||||
{
|
||||
data = list->addr;
|
||||
if ((setup->wValue >> 8) == 3)
|
||||
{
|
||||
// for string descriptors, use the descriptor's
|
||||
// length field, allowing runtime configured
|
||||
// length.
|
||||
datalen = *(list->addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
datalen = list->length;
|
||||
}
|
||||
goto send;
|
||||
}
|
||||
}
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
|
||||
case (MSFT_VENDOR_CODE << 8) | 0xC0: // Get Microsoft descriptor
|
||||
case (MSFT_VENDOR_CODE << 8) | 0xC1:
|
||||
if (setup->wIndex == 0x0004)
|
||||
{
|
||||
// Return WCID descriptor
|
||||
data = usb_microsoft_wcid;
|
||||
datalen = MSFT_WCID_LEN;
|
||||
break;
|
||||
}
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
|
||||
case (WEBUSB_VENDOR_CODE << 8) | 0xC0: // Get WebUSB descriptor
|
||||
if (setup->wIndex == 0x0002)
|
||||
{
|
||||
if (setup->wValue == 0x0001)
|
||||
{
|
||||
// Return landing page URL descriptor
|
||||
data = (uint8_t*)&landing_url_descriptor;
|
||||
datalen = LANDING_PAGE_DESCRIPTOR_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// printf("%s:%d couldn't find webusb descriptor (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue);
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
|
||||
case 0x0121: // DFU_DNLOAD
|
||||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
// Data comes in the OUT phase. But if it's a zero-length request, handle it now.
|
||||
if (setup->wLength == 0)
|
||||
{
|
||||
if (!dfu_download(setup->wValue, 0, 0, 0, NULL))
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
usb_ack(dev, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// ACK the setup packet
|
||||
usb_ack(dev, 0);
|
||||
|
||||
int bytes_remaining = setup->wLength;
|
||||
int ep0_rx_offset = 0;
|
||||
while (bytes_remaining > 0) {
|
||||
|
||||
// Fill the buffer, or if there is enough space transfer the whole packet.
|
||||
unsigned int len = setup->wLength;
|
||||
if (len > sizeof(rx_buffer))
|
||||
len = sizeof(rx_buffer);
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(rx_buffer)/4; i++)
|
||||
rx_buffer[i] = 0xffffffff;
|
||||
|
||||
// Receive DATA packets (which are automatically ACKed)
|
||||
len = usb_recv(dev, (void *)rx_buffer, len);
|
||||
|
||||
// Append the data to the download buffer.
|
||||
dfu_download(setup->wValue, setup->wLength, ep0_rx_offset, len, (void *)rx_buffer);
|
||||
|
||||
bytes_remaining -= len;
|
||||
ep0_rx_offset += len;
|
||||
}
|
||||
return;
|
||||
|
||||
case 0x0021: // DFU_DETACH
|
||||
// Send the "ACK" packet and wait for it
|
||||
// to be received.
|
||||
usb_ack(dev, 0);
|
||||
usb_wait_for_send_done(dev);
|
||||
usb_disconnect();
|
||||
|
||||
// Issue a reboot
|
||||
reboot_to_image(0);
|
||||
while (1)
|
||||
;
|
||||
return;
|
||||
|
||||
case 0x03a1: // DFU_GETSTATUS
|
||||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
if (dfu_getstatus(reply_buffer))
|
||||
{
|
||||
data = reply_buffer;
|
||||
datalen = 6;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0421: // DFU_CLRSTATUS
|
||||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
if (dfu_clrstatus())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x05a1: // DFU_GETSTATE
|
||||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
reply_buffer[0] = dfu_getstate();
|
||||
data = reply_buffer;
|
||||
datalen = 1;
|
||||
break;
|
||||
|
||||
case 0x0621: // DFU_ABORT
|
||||
if (setup->wIndex > 0)
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
if (dfu_abort())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
usb_err(dev, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
send:
|
||||
if (data && datalen) {
|
||||
if (datalen > setup->wLength)
|
||||
datalen = setup->wLength;
|
||||
usb_send(dev, 0, data, datalen);
|
||||
}
|
||||
else
|
||||
usb_ack(dev, 0);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
#include <usb.h>
|
||||
#include <irq.h>
|
||||
#include <generated/csr.h>
|
||||
#include <string.h>
|
||||
#include <printf.h>
|
||||
#include <uart.h>
|
||||
#include <usb.h>
|
||||
|
||||
#ifdef CSR_USB_EP_0_OUT_EV_PENDING_ADDR
|
||||
|
||||
/* The state machine states of a control pipe */
|
||||
enum CONTROL_STATE
|
||||
{
|
||||
WAIT_SETUP,
|
||||
IN_SETUP,
|
||||
IN_DATA,
|
||||
OUT_DATA,
|
||||
LAST_IN_DATA,
|
||||
WAIT_STATUS_IN,
|
||||
WAIT_STATUS_OUT,
|
||||
STALLED,
|
||||
} control_state;
|
||||
|
||||
// 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_disconnect(void) {
|
||||
usb_ep_0_out_ev_enable_write(0);
|
||||
usb_ep_0_in_ev_enable_write(0);
|
||||
irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT));
|
||||
usb_pullup_out_write(0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// By default, it wants to respond with NAK.
|
||||
usb_ep_0_out_respond_write(EPF_ACK);
|
||||
usb_ep_0_in_respond_write(EPF_ACK);
|
||||
|
||||
usb_pullup_out_write(1);
|
||||
|
||||
irq_setmask(irq_getmask() | (1 << USB_INTERRUPT));
|
||||
}
|
||||
|
||||
void usb_init(void) {
|
||||
usb_pullup_out_write(0);
|
||||
return;
|
||||
}
|
||||
|
||||
static volatile int irq_count = 0;
|
||||
|
||||
#define EP0OUT_BUFFERS 8
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t usb_ep0out_buffer[EP0OUT_BUFFERS][256];
|
||||
static uint8_t usb_ep0out_buffer_len[EP0OUT_BUFFERS];
|
||||
static uint8_t 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;
|
||||
|
||||
static const uint8_t *current_data;
|
||||
static int current_length;
|
||||
static int current_offset;
|
||||
static int current_to_send;
|
||||
|
||||
static int queue_more_data(int epnum) {
|
||||
(void)epnum;
|
||||
|
||||
// Don't allow requeueing -- only queue more data if we're
|
||||
// currently set up to respond NAK.
|
||||
if (usb_ep_0_in_respond_read() != EPF_NAK)
|
||||
return -1;
|
||||
|
||||
// Prevent us from double-filling the buffer.
|
||||
if (!usb_ep_0_in_ibuf_empty_read()) {
|
||||
usb_ep_0_in_respond_write(EPF_ACK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int this_offset;
|
||||
current_to_send = current_length - current_offset;
|
||||
if (current_to_send > max_byte_length)
|
||||
current_to_send = max_byte_length;
|
||||
|
||||
for (this_offset = current_offset; this_offset < (current_offset + current_to_send); this_offset++) {
|
||||
usb_ep_0_in_ibuf_head_write(current_data[this_offset]);
|
||||
}
|
||||
usb_ep_0_in_respond_write(EPF_ACK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_send(struct usb_device *dev, int epnum, const void *data, int total_count) {
|
||||
(void)dev;
|
||||
|
||||
while (current_data || current_length)
|
||||
;
|
||||
current_data = (uint8_t *)data;
|
||||
current_length = total_count;
|
||||
current_offset = 0;
|
||||
control_state = IN_DATA;
|
||||
queue_more_data(epnum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_wait_for_send_done(struct usb_device *dev) {
|
||||
while (current_data && current_length)
|
||||
usb_poll(dev);
|
||||
while ((usb_ep_0_in_dtb_read() & 1) == 1)
|
||||
usb_poll(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_isr(void) {
|
||||
irq_count++;
|
||||
uint8_t ep0o_pending = usb_ep_0_out_ev_pending_read();
|
||||
uint8_t ep0i_pending = usb_ep_0_in_ev_pending_read();
|
||||
|
||||
// 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;
|
||||
uint8_t *obuf = usb_ep0out_buffer[usb_ep0out_wr_ptr];
|
||||
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);
|
||||
}
|
||||
usb_ep_0_out_ev_pending_write(ep0o_pending);
|
||||
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) {
|
||||
current_offset = 0;
|
||||
current_length = 0;
|
||||
current_data = NULL;
|
||||
control_state = IN_SETUP;
|
||||
}
|
||||
}
|
||||
|
||||
// We just got an "IN" token. Send data if we have it.
|
||||
if (ep0i_pending) {
|
||||
usb_ep_0_in_respond_write(EPF_NAK);
|
||||
current_offset += current_to_send;
|
||||
queue_more_data(0);
|
||||
usb_ep_0_in_ev_pending_write(ep0i_pending);
|
||||
usb_ep_0_out_respond_write(EPF_ACK);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void usb_wait(void) {
|
||||
while (!irq_count)
|
||||
;
|
||||
}
|
||||
|
||||
int usb_irq_happened(void) {
|
||||
return irq_count;
|
||||
}
|
||||
|
||||
int usb_ack(struct usb_device *dev, int epnum) {
|
||||
(void)dev;
|
||||
(void)epnum;
|
||||
usb_ep_0_in_dtb_write(1);
|
||||
usb_ep_0_out_dtb_write(1);
|
||||
usb_ep_0_out_respond_write(EPF_ACK);
|
||||
usb_ep_0_in_respond_write(EPF_ACK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_err(struct usb_device *dev, int epnum) {
|
||||
(void)dev;
|
||||
(void)epnum;
|
||||
usb_ep_0_out_respond_write(EPF_STALL);
|
||||
usb_ep_0_in_respond_write(EPF_STALL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// int puts_noendl(const char *s);
|
||||
// static void print_eptype(void) {
|
||||
// switch (usb_ep0out_last_tok[usb_ep0out_rd_ptr]) {
|
||||
// case 0: puts("O"); break;
|
||||
// // case 1: puts("SOF"); break;
|
||||
// // case 2: puts("IN"); break;
|
||||
// case 3: puts("S"); break;
|
||||
// }
|
||||
// }
|
||||
|
||||
int usb_recv(struct usb_device *dev, void *buffer, unsigned int buffer_len) {
|
||||
(void)dev;
|
||||
|
||||
// Set the OUT response to ACK, since we are in a position to receive data now.
|
||||
usb_ep_0_out_respond_write(EPF_ACK);
|
||||
while (1) {
|
||||
if (usb_ep0out_rd_ptr != usb_ep0out_wr_ptr) {
|
||||
if (usb_ep0out_last_tok[usb_ep0out_rd_ptr] == USB_PID_OUT) {
|
||||
unsigned int ep0_buffer_len = usb_ep0out_buffer_len[usb_ep0out_rd_ptr];
|
||||
if (ep0_buffer_len < buffer_len)
|
||||
buffer_len = ep0_buffer_len;
|
||||
usb_ep0out_buffer_len[usb_ep0out_rd_ptr] = 0;
|
||||
memcpy(buffer, &usb_ep0out_buffer[usb_ep0out_rd_ptr], buffer_len);
|
||||
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
|
||||
return buffer_len;
|
||||
}
|
||||
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_poll(struct usb_device *dev) {
|
||||
(void)dev;
|
||||
// If some data was received, then process it.
|
||||
if (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]);
|
||||
// unsigned int 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(NULL, request);
|
||||
}
|
||||
}
|
||||
|
||||
if ((usb_ep_0_in_respond_read() == EPF_NAK) && (current_data)) {
|
||||
current_offset += current_to_send;
|
||||
queue_more_data(0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CSR_USB_EP_0_OUT_EV_PENDING_ADDR */
|
|
@ -0,0 +1,225 @@
|
|||
#include <usb.h>
|
||||
#include <irq.h>
|
||||
#include <generated/csr.h>
|
||||
#include <string.h>
|
||||
#include <printf.h>
|
||||
#include <uart.h>
|
||||
|
||||
#ifdef CSR_USB_OBUF_EMPTY_ADDR
|
||||
|
||||
static const uint8_t crc5Table4[] =
|
||||
{
|
||||
0x00, 0x0E, 0x1C, 0x12, 0x11, 0x1F, 0x0D, 0x03,
|
||||
0x0B, 0x05, 0x17, 0x19, 0x1A, 0x14, 0x06, 0x08};
|
||||
static const uint8_t crc5Table0[] =
|
||||
{
|
||||
0x00, 0x16, 0x05, 0x13, 0x0A, 0x1C, 0x0F, 0x19,
|
||||
0x14, 0x02, 0x11, 0x07, 0x1E, 0x08, 0x1B, 0x0D};
|
||||
//---------------
|
||||
static int crc5Check(const uint8_t *data)
|
||||
//---------------
|
||||
{
|
||||
uint8_t b = data[0] ^ 0x1F;
|
||||
uint8_t crc = crc5Table4[b & 0x0F] ^ crc5Table0[(b >> 4) & 0x0F];
|
||||
b = data[1] ^ crc;
|
||||
return (crc5Table4[b & 0x0F] ^ crc5Table0[(b >> 4) & 0x0F]) == 0x06;
|
||||
}
|
||||
// crc5Check
|
||||
|
||||
static int do_check(uint16_t pkt) {
|
||||
uint8_t data[2] = {
|
||||
pkt >> 8,
|
||||
pkt,
|
||||
};
|
||||
return crc5Check(data);
|
||||
}
|
||||
|
||||
#define INT_SIZE 32
|
||||
static unsigned CRC5(unsigned dwInput, int iBitcnt)
|
||||
{
|
||||
const uint32_t poly5 = (0x05 << (INT_SIZE-5));
|
||||
uint32_t crc5 = (0x1f << (INT_SIZE-5));
|
||||
uint32_t udata = (dwInput << (INT_SIZE-iBitcnt));
|
||||
|
||||
if ( (iBitcnt<1) || (iBitcnt>INT_SIZE) ) // Validate iBitcnt
|
||||
return 0xffffffff;
|
||||
|
||||
while (iBitcnt--)
|
||||
{
|
||||
if ( (udata ^ crc5) & (0x1<<(INT_SIZE-1)) ) // bit4 != bit4?
|
||||
{
|
||||
crc5 <<= 1;
|
||||
crc5 ^= poly5;
|
||||
}
|
||||
else
|
||||
crc5 <<= 1;
|
||||
|
||||
udata <<= 1;
|
||||
}
|
||||
|
||||
// Shift back into position
|
||||
crc5 >>= (INT_SIZE-5);
|
||||
|
||||
// Invert contents to generate crc field
|
||||
crc5 ^= 0x1f;
|
||||
|
||||
return crc5;
|
||||
} //CRC5()
|
||||
|
||||
static uint32_t reverse_sof(uint32_t data) {
|
||||
int i;
|
||||
uint32_t data_flipped = 0;
|
||||
for (i = 0; i < 11; i++)
|
||||
if (data & (1 << i))
|
||||
data_flipped |= 1 << (10 - i);
|
||||
|
||||
return data_flipped;
|
||||
}
|
||||
|
||||
static uint8_t reverse_byte(uint8_t data) {
|
||||
int i;
|
||||
uint8_t data_flipped = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
if (data & (1 << i))
|
||||
data_flipped |= 1 << (7 - i);
|
||||
|
||||
return data_flipped;
|
||||
}
|
||||
|
||||
static uint8_t reverse_crc5(uint8_t data) {
|
||||
int i;
|
||||
uint8_t data_flipped = 0;
|
||||
for (i = 0; i < 5; i++)
|
||||
if (data & (1 << i))
|
||||
data_flipped |= 1 << (4 - i);
|
||||
|
||||
return data_flipped;
|
||||
}
|
||||
|
||||
static uint16_t make_token(uint16_t data) {
|
||||
uint16_t val = 0;
|
||||
|
||||
data = reverse_sof(data);
|
||||
val = data << 5;
|
||||
val |= CRC5(data, 11);
|
||||
|
||||
return (reverse_byte(val >> 8) << 8) | reverse_byte(val);
|
||||
}
|
||||
|
||||
int do_crc5(uint8_t bfr[2]) {
|
||||
uint8_t pkt_flipped[2] = {
|
||||
reverse_byte(bfr[0]),
|
||||
reverse_byte(bfr[1]),
|
||||
};
|
||||
uint32_t data = (pkt_flipped[1] >> 5) | (pkt_flipped[0] << 3);
|
||||
uint32_t data_flipped;
|
||||
uint8_t crc;
|
||||
uint16_t pkt;
|
||||
((uint8_t *)&pkt)[0] = bfr[1];
|
||||
((uint8_t *)&pkt)[1] = bfr[0];
|
||||
uint8_t found_crc = (pkt >> 3) & 0x1f;
|
||||
|
||||
data_flipped = reverse_sof(data);
|
||||
|
||||
crc = CRC5(data, 11);
|
||||
crc = reverse_crc5(crc);
|
||||
|
||||
uint16_t reconstructed = make_token(data_flipped);
|
||||
uint16_t wire = (reverse_byte(pkt >> 8) << 8) | reverse_byte(pkt);
|
||||
|
||||
printf("Packet: 0x%04x FCRC: %02x Data: 0x%04x "
|
||||
"Flipped: 0x%04x CRC5: 0x%02x Pass? %d Reconstructed: 0x%04x Wire: %04x\n",
|
||||
pkt, found_crc, data, data_flipped, crc, do_check(pkt),
|
||||
reconstructed,
|
||||
wire
|
||||
);
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
|
||||
uint8_t usb_ep0out_wr_ptr;
|
||||
uint8_t usb_ep0out_rd_ptr;
|
||||
#define EP0OUT_BUFFERS 64
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t usb_ep0out_buffer[EP0OUT_BUFFERS][128];
|
||||
static uint8_t usb_ep0out_buffer_len[EP0OUT_BUFFERS];
|
||||
void usb_poll(void)
|
||||
{
|
||||
// usb_isr();
|
||||
// printf("Start byte_count: %d\n", usb_byte_count_read());
|
||||
while (usb_ep0out_rd_ptr != usb_ep0out_wr_ptr) {
|
||||
uint8_t *obuf = usb_ep0out_buffer[usb_ep0out_rd_ptr];
|
||||
uint8_t cnt = usb_ep0out_buffer_len[usb_ep0out_rd_ptr];
|
||||
unsigned int i;
|
||||
if (cnt) {
|
||||
for (i = 0; i < cnt; i++) {
|
||||
uart_write(' ');
|
||||
uart_write(hex[(obuf[i] >> 4) & 0xf]);
|
||||
uart_write(hex[obuf[i] & (0xf)]);
|
||||
}
|
||||
uart_write('\r');
|
||||
uart_write('\n');
|
||||
}
|
||||
if (obuf[0] == 0xa5) {
|
||||
do_crc5(obuf + 1);
|
||||
}
|
||||
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
|
||||
}
|
||||
}
|
||||
|
||||
int irq_happened;
|
||||
|
||||
void usb_init(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
int usb_send(struct usb_device *dev, int epnum, const void *data, int total_count) {
|
||||
unsigned int i;
|
||||
const uint8_t *data_bfr = data;
|
||||
while (!usb_ibuf_empty_read())
|
||||
printf(".");
|
||||
usb_arm_write(0);
|
||||
for (i = 0; i < total_count; i++) {
|
||||
printf("Writing %02x ", data_bfr[i]);
|
||||
usb_ibuf_head_write(data_bfr[i]);
|
||||
}
|
||||
usb_arm_write(1);
|
||||
}
|
||||
|
||||
void usb_isr(void) {
|
||||
uint8_t pending = usb_ev_pending_read();
|
||||
unsigned int byte_count = 0;
|
||||
|
||||
// printf("Start pending: %d byte_count: %d empty: %d\n", pending, usb_byte_count_read(), usb_obuf_empty_read());
|
||||
// Advance the obuf head, which will reset the obuf_empty bit
|
||||
if (pending & 1) {
|
||||
uint8_t *obuf = usb_ep0out_buffer[usb_ep0out_wr_ptr];
|
||||
while (!usb_obuf_empty_read() && (byte_count < sizeof(usb_ep0out_buffer[usb_ep0out_wr_ptr]))) {
|
||||
obuf[byte_count++] = usb_obuf_head_read();
|
||||
usb_obuf_head_write(0);
|
||||
}
|
||||
usb_ep0out_buffer_len[usb_ep0out_wr_ptr] = byte_count;
|
||||
usb_ep0out_wr_ptr = (usb_ep0out_wr_ptr + 1) & (EP0OUT_BUFFERS-1);
|
||||
usb_ev_pending_write(pending);
|
||||
}
|
||||
// printf("Start pending: %d byte_count: %d empty: %d bytes_read: %d\n", pending, usb_byte_count_read(), usb_obuf_empty_read(), byte_count);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void usb_connect(void) {
|
||||
usb_pullup_out_write(1);
|
||||
|
||||
usb_ev_pending_write(usb_ev_pending_read());
|
||||
usb_ev_enable_write(1);
|
||||
|
||||
irq_setmask(irq_getmask() | (1 << USB_INTERRUPT));
|
||||
}
|
||||
|
||||
int usb_irq_happened(void) {
|
||||
return irq_happened;
|
||||
}
|
||||
|
||||
#endif /* CSR_USB_OBUF_EMPTY_ADDR */
|
|
@ -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,5 +1,7 @@
|
|||
.global main
|
||||
.global isr
|
||||
|
||||
.section .text.start
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
|
@ -12,6 +14,7 @@ _start:
|
|||
nop
|
||||
nop
|
||||
|
||||
.section .text
|
||||
.global trap_entry
|
||||
trap_entry:
|
||||
sw x1, - 1*4(sp)
|
||||
|
@ -68,6 +71,18 @@ bss_loop:
|
|||
j bss_loop
|
||||
bss_done:
|
||||
|
||||
/* Load DATA */
|
||||
la t0, _erodata
|
||||
la t1, _fdata
|
||||
la t2, _edata
|
||||
3:
|
||||
lw t3, 0(t0)
|
||||
sw t3, 0(t1)
|
||||
/* _edata is aligned to 16 bytes. Use word-xfers. */
|
||||
addi t0, t0, 4
|
||||
addi t1, t1, 4
|
||||
bltu t1, t2, 3b
|
||||
|
||||
li a0, 0x880 //880 enable timer + external interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
|
||||
csrw mie,a0
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/* $OpenBSD: strlen.c,v 1.8 2014/06/10 04:17:37 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
size_t
|
||||
strlen(const char *str)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
for (s = str; *s; ++s)
|
||||
;
|
||||
return (s - str);
|
||||
}
|
||||
|
||||
/**
|
||||
* memcpy - Copies one area of memory to another
|
||||
* @dest: Destination
|
||||
* @src: Source
|
||||
* @n: The size to copy.
|
||||
*/
|
||||
void *memcpy(void *to, const void *from, size_t n)
|
||||
{
|
||||
void *xto = to;
|
||||
size_t temp;
|
||||
|
||||
if(!n)
|
||||
return xto;
|
||||
if((long)to & 1) {
|
||||
char *cto = to;
|
||||
const char *cfrom = from;
|
||||
*cto++ = *cfrom++;
|
||||
to = cto;
|
||||
from = cfrom;
|
||||
n--;
|
||||
}
|
||||
if((long)from & 1) {
|
||||
char *cto = to;
|
||||
const char *cfrom = from;
|
||||
for (; n; n--)
|
||||
*cto++ = *cfrom++;
|
||||
return xto;
|
||||
}
|
||||
if(n > 2 && (long)to & 2) {
|
||||
short *sto = to;
|
||||
const short *sfrom = from;
|
||||
*sto++ = *sfrom++;
|
||||
to = sto;
|
||||
from = sfrom;
|
||||
n -= 2;
|
||||
}
|
||||
if((long)from & 2) {
|
||||
short *sto = to;
|
||||
const short *sfrom = from;
|
||||
temp = n >> 1;
|
||||
for (; temp; temp--)
|
||||
*sto++ = *sfrom++;
|
||||
to = sto;
|
||||
from = sfrom;
|
||||
if(n & 1) {
|
||||
char *cto = to;
|
||||
const char *cfrom = from;
|
||||
*cto = *cfrom;
|
||||
}
|
||||
return xto;
|
||||
}
|
||||
temp = n >> 2;
|
||||
if(temp) {
|
||||
long *lto = to;
|
||||
const long *lfrom = from;
|
||||
for(; temp; temp--)
|
||||
*lto++ = *lfrom++;
|
||||
to = lto;
|
||||
from = lfrom;
|
||||
}
|
||||
if(n & 2) {
|
||||
short *sto = to;
|
||||
const short *sfrom = from;
|
||||
*sto++ = *sfrom++;
|
||||
to = sto;
|
||||
from = sfrom;
|
||||
}
|
||||
if(n & 1) {
|
||||
char *cto = to;
|
||||
const char *cfrom = from;
|
||||
*cto = *cfrom;
|
||||
}
|
||||
return xto;
|
||||
}
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define min(a, b) (a) < (b) ? a : b
|
||||
#define min(a, b) ((unsigned)(a)) < (((unsigned)(b))) ? ((unsigned)(a)) : ((unsigned)(b))
|
||||
|
||||
#define swapcode(TYPE, parmi, parmj, n) \
|
||||
{ \
|
||||
|
@ -199,12 +199,12 @@ loop:
|
|||
r = min(pd - pc, pn - pd - size);
|
||||
vecswap(pb, pn - r, r);
|
||||
|
||||
if ((r = pb - pa) > size)
|
||||
if ((r = pb - pa) > (int)size)
|
||||
{
|
||||
qsort(base, r / size, size, compar);
|
||||
}
|
||||
|
||||
if ((r = pd - pc) > size)
|
||||
if ((r = pd - pc) > (int)size)
|
||||
{
|
||||
/* Iterate rather than recurse to save stack space */
|
||||
base = pn - r;
|
|
@ -3,6 +3,7 @@
|
|||
#include <generated/csr.h>
|
||||
#include <hw/flags.h>
|
||||
|
||||
#ifdef CSR_UART_BASE
|
||||
/*
|
||||
* Buffer sizes must be a power of 2 so that modulos can be computed
|
||||
* with logical AND.
|
||||
|
@ -53,8 +54,8 @@ char uart_read(void)
|
|||
{
|
||||
char c;
|
||||
|
||||
if(irq_getie()) {
|
||||
while(rx_consume == rx_produce);
|
||||
if (irq_getie()) {
|
||||
while (rx_consume == rx_produce);
|
||||
} else if (rx_consume == rx_produce) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -74,7 +75,7 @@ void uart_write(char c)
|
|||
unsigned int oldmask;
|
||||
unsigned int tx_produce_next = (tx_produce + 1) & UART_RINGBUFFER_MASK_TX;
|
||||
|
||||
if(irq_getie()) {
|
||||
if (irq_getie()) {
|
||||
while(tx_produce_next == tx_consume);
|
||||
} else if(tx_produce_next == tx_consume) {
|
||||
return;
|
||||
|
@ -82,7 +83,7 @@ void uart_write(char c)
|
|||
|
||||
oldmask = irq_getmask();
|
||||
irq_setmask(oldmask & ~(1 << UART_INTERRUPT));
|
||||
if((tx_consume != tx_produce) || uart_txfull_read()) {
|
||||
if ((tx_consume != tx_produce) || uart_txfull_read()) {
|
||||
tx_buf[tx_produce] = c;
|
||||
tx_produce = tx_produce_next;
|
||||
} else {
|
||||
|
@ -108,3 +109,7 @@ void uart_sync(void)
|
|||
{
|
||||
while(tx_consume != tx_produce);
|
||||
}
|
||||
#else /* !CSR_UART_BASE */
|
||||
void uart_init(void) {}
|
||||
void uart_isr(void) {}
|
||||
#endif
|
|
@ -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,268 @@
|
|||
/*
|
||||
* Copyright (c) 2004,2012 Kustaa Nyholm / SpareTimeLabs
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the Kustaa Nyholm or SpareTimeLabs nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "printf.h"
|
||||
|
||||
typedef void (*putcf)(void *, char);
|
||||
static putcf stdout_putf;
|
||||
static void *stdout_putp;
|
||||
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
|
||||
static void uli2a(unsigned long int num, unsigned int base, int uc, char *bf)
|
||||
{
|
||||
int n = 0;
|
||||
unsigned int d = 1;
|
||||
while (num / d >= base)
|
||||
d *= base;
|
||||
while (d != 0)
|
||||
{
|
||||
int dgt = num / d;
|
||||
num %= d;
|
||||
d /= base;
|
||||
if (n || dgt > 0 || d == 0)
|
||||
{
|
||||
*bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
*bf = 0;
|
||||
}
|
||||
|
||||
static void li2a(long num, char *bf)
|
||||
{
|
||||
if (num < 0)
|
||||
{
|
||||
num = -num;
|
||||
*bf++ = '-';
|
||||
}
|
||||
uli2a(num, 10, 0, bf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void ui2a(unsigned int num, unsigned int base, int uc, char *bf)
|
||||
{
|
||||
int n = 0;
|
||||
unsigned int d = 1;
|
||||
while (num / d >= base)
|
||||
d *= base;
|
||||
while (d != 0)
|
||||
{
|
||||
int dgt = num / d;
|
||||
num %= d;
|
||||
d /= base;
|
||||
if (n || dgt > 0 || d == 0)
|
||||
{
|
||||
*bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
*bf = 0;
|
||||
}
|
||||
|
||||
static void i2a(int num, char *bf)
|
||||
{
|
||||
if (num < 0)
|
||||
{
|
||||
num = -num;
|
||||
*bf++ = '-';
|
||||
}
|
||||
ui2a(num, 10, 0, bf);
|
||||
}
|
||||
|
||||
static int a2d(char ch)
|
||||
{
|
||||
if (ch >= '0' && ch <= '9')
|
||||
return ch - '0';
|
||||
else if (ch >= 'a' && ch <= 'f')
|
||||
return ch - 'a' + 10;
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
return ch - 'A' + 10;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char a2i(char ch, char **src, int base, int *nump)
|
||||
{
|
||||
char *p = *src;
|
||||
int num = 0;
|
||||
int digit;
|
||||
while ((digit = a2d(ch)) >= 0)
|
||||
{
|
||||
if (digit > base)
|
||||
break;
|
||||
num = num * base + digit;
|
||||
ch = *p++;
|
||||
}
|
||||
*src = p;
|
||||
*nump = num;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static void putchw(void *putp, putcf putf, int n, char z, char *bf)
|
||||
{
|
||||
char fc = z ? '0' : ' ';
|
||||
char ch;
|
||||
char *p = bf;
|
||||
while (*p++ && n > 0)
|
||||
n--;
|
||||
while (n-- > 0)
|
||||
putf(putp, fc);
|
||||
while ((ch = *bf++))
|
||||
putf(putp, ch);
|
||||
}
|
||||
|
||||
void tfp_format(void *putp, putcf putf, char *fmt, va_list va)
|
||||
{
|
||||
char bf[12];
|
||||
|
||||
char ch;
|
||||
|
||||
while ((ch = *(fmt++)))
|
||||
{
|
||||
if (ch != '%')
|
||||
putf(putp, ch);
|
||||
else
|
||||
{
|
||||
char lz = 0;
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
char lng = 0;
|
||||
#endif
|
||||
int w = 0;
|
||||
ch = *(fmt++);
|
||||
if (ch == '0')
|
||||
{
|
||||
ch = *(fmt++);
|
||||
lz = 1;
|
||||
}
|
||||
if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
ch = a2i(ch, &fmt, 10, &w);
|
||||
}
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (ch == 'l')
|
||||
{
|
||||
ch = *(fmt++);
|
||||
lng = 1;
|
||||
}
|
||||
#endif
|
||||
switch (ch)
|
||||
{
|
||||
case 0:
|
||||
goto abort;
|
||||
case 'u':
|
||||
{
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (lng)
|
||||
uli2a(va_arg(va, unsigned long int), 10, 0, bf);
|
||||
else
|
||||
#endif
|
||||
ui2a(va_arg(va, unsigned int), 10, 0, bf);
|
||||
putchw(putp, putf, w, lz, bf);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (lng)
|
||||
li2a(va_arg(va, unsigned long int), bf);
|
||||
else
|
||||
#endif
|
||||
i2a(va_arg(va, int), bf);
|
||||
putchw(putp, putf, w, lz, bf);
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'X':
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (lng)
|
||||
uli2a(va_arg(va, unsigned long int), 16, (ch == 'X'), bf);
|
||||
else
|
||||
#endif
|
||||
ui2a(va_arg(va, unsigned int), 16, (ch == 'X'), bf);
|
||||
putchw(putp, putf, w, lz, bf);
|
||||
break;
|
||||
case 'c':
|
||||
putf(putp, (char)(va_arg(va, int)));
|
||||
break;
|
||||
case 's':
|
||||
putchw(putp, putf, w, 0, va_arg(va, char *));
|
||||
break;
|
||||
case '%':
|
||||
putf(putp, ch);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
abort:;
|
||||
}
|
||||
|
||||
void init_printf(void *putp, void (*putf)(void *, char))
|
||||
{
|
||||
stdout_putf = putf;
|
||||
stdout_putp = putp;
|
||||
}
|
||||
|
||||
void tfp_printf(char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
tfp_format(stdout_putp, stdout_putf, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static void putcp(void *p, char c)
|
||||
{
|
||||
*(*((char **)p))++ = c;
|
||||
}
|
||||
|
||||
void tfp_sprintf(char *s, char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
tfp_format(&s, putcp, fmt, va);
|
||||
putcp(&s, 0);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
int puts(const char *s) {
|
||||
puts_noendl(s);
|
||||
stdout_putf(stdout_putp, '\n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
int puts_noendl(const char *s) {
|
||||
while (*s)
|
||||
stdout_putf(stdout_putp, *s++);
|
||||
return 1;
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004,2012 Kustaa Nyholm / SpareTimeLabs
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the Kustaa Nyholm or SpareTimeLabs nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "printf.h"
|
||||
|
||||
typedef void (*putcf) (void*,char);
|
||||
static putcf stdout_putf;
|
||||
static void* stdout_putp;
|
||||
|
||||
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
|
||||
static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf)
|
||||
{
|
||||
int n=0;
|
||||
unsigned int d=1;
|
||||
while (num/d >= base)
|
||||
d*=base;
|
||||
while (d!=0) {
|
||||
int dgt = num / d;
|
||||
num%=d;
|
||||
d/=base;
|
||||
if (n || dgt>0|| d==0) {
|
||||
*bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
*bf=0;
|
||||
}
|
||||
|
||||
static void li2a (long num, char * bf)
|
||||
{
|
||||
if (num<0) {
|
||||
num=-num;
|
||||
*bf++ = '-';
|
||||
}
|
||||
uli2a(num,10,0,bf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void ui2a(unsigned int num, unsigned int base, int uc,char * bf)
|
||||
{
|
||||
int n=0;
|
||||
unsigned int d=1;
|
||||
while (num/d >= base)
|
||||
d*=base;
|
||||
while (d!=0) {
|
||||
int dgt = num / d;
|
||||
num%= d;
|
||||
d/=base;
|
||||
if (n || dgt>0 || d==0) {
|
||||
*bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
*bf=0;
|
||||
}
|
||||
|
||||
static void i2a (int num, char * bf)
|
||||
{
|
||||
if (num<0) {
|
||||
num=-num;
|
||||
*bf++ = '-';
|
||||
}
|
||||
ui2a(num,10,0,bf);
|
||||
}
|
||||
|
||||
static int a2d(char ch)
|
||||
{
|
||||
if (ch>='0' && ch<='9')
|
||||
return ch-'0';
|
||||
else if (ch>='a' && ch<='f')
|
||||
return ch-'a'+10;
|
||||
else if (ch>='A' && ch<='F')
|
||||
return ch-'A'+10;
|
||||
else return -1;
|
||||
}
|
||||
|
||||
static char a2i(char ch, char** src,int base,int* nump)
|
||||
{
|
||||
char* p= *src;
|
||||
int num=0;
|
||||
int digit;
|
||||
while ((digit=a2d(ch))>=0) {
|
||||
if (digit>base) break;
|
||||
num=num*base+digit;
|
||||
ch=*p++;
|
||||
}
|
||||
*src=p;
|
||||
*nump=num;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static void putchw(void* putp,putcf putf,int n, char z, char* bf)
|
||||
{
|
||||
char fc=z? '0' : ' ';
|
||||
char ch;
|
||||
char* p=bf;
|
||||
while (*p++ && n > 0)
|
||||
n--;
|
||||
while (n-- > 0)
|
||||
putf(putp,fc);
|
||||
while ((ch= *bf++))
|
||||
putf(putp,ch);
|
||||
}
|
||||
|
||||
void tfp_format(void* putp,putcf putf,char *fmt, va_list va)
|
||||
{
|
||||
char bf[12];
|
||||
|
||||
char ch;
|
||||
|
||||
|
||||
while ((ch=*(fmt++))) {
|
||||
if (ch!='%')
|
||||
putf(putp,ch);
|
||||
else {
|
||||
char lz=0;
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
char lng=0;
|
||||
#endif
|
||||
int w=0;
|
||||
ch=*(fmt++);
|
||||
if (ch=='0') {
|
||||
ch=*(fmt++);
|
||||
lz=1;
|
||||
}
|
||||
if (ch>='0' && ch<='9') {
|
||||
ch=a2i(ch,&fmt,10,&w);
|
||||
}
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (ch=='l') {
|
||||
ch=*(fmt++);
|
||||
lng=1;
|
||||
}
|
||||
#endif
|
||||
switch (ch) {
|
||||
case 0:
|
||||
goto abort;
|
||||
case 'u' : {
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (lng)
|
||||
uli2a(va_arg(va, unsigned long int),10,0,bf);
|
||||
else
|
||||
#endif
|
||||
ui2a(va_arg(va, unsigned int),10,0,bf);
|
||||
putchw(putp,putf,w,lz,bf);
|
||||
break;
|
||||
}
|
||||
case 'd' : {
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (lng)
|
||||
li2a(va_arg(va, unsigned long int),bf);
|
||||
else
|
||||
#endif
|
||||
i2a(va_arg(va, int),bf);
|
||||
putchw(putp,putf,w,lz,bf);
|
||||
break;
|
||||
}
|
||||
case 'x': case 'X' :
|
||||
#ifdef PRINTF_LONG_SUPPORT
|
||||
if (lng)
|
||||
uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf);
|
||||
else
|
||||
#endif
|
||||
ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf);
|
||||
putchw(putp,putf,w,lz,bf);
|
||||
break;
|
||||
case 'c' :
|
||||
putf(putp,(char)(va_arg(va, int)));
|
||||
break;
|
||||
case 's' :
|
||||
putchw(putp,putf,w,0,va_arg(va, char*));
|
||||
break;
|
||||
case '%' :
|
||||
putf(putp,ch);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
abort:;
|
||||
}
|
||||
|
||||
|
||||
void init_printf(void* putp,void (*putf) (void*,char))
|
||||
{
|
||||
stdout_putf=putf;
|
||||
stdout_putp=putp;
|
||||
}
|
||||
|
||||
void tfp_printf(char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,fmt);
|
||||
tfp_format(stdout_putp,stdout_putf,fmt,va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static void putcp(void* p,char c)
|
||||
{
|
||||
*(*((char**)p))++ = c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void tfp_sprintf(char* s,char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,fmt);
|
||||
tfp_format(&s,putcp,fmt,va);
|
||||
putcp(&s,0);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue