Compare commits

...

127 Commits

Author SHA1 Message Date
Sean Cross c0df98f66e hw: correct crystal pin for Hacker board
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 17:08:01 +08:00
Sean Cross 6d9028f505 hw: foboot-bitstream: create multiboot image after build
This fixes an issue where the directory wasn't created first.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 16:19:30 +08:00
Sean Cross 7ee97214e6 foboot-bitstream: generate multiboot and print helpful message
Generate a multiboot version of the bitstream image.  While we're at it,
print a helpful message indicating what each output image is.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 11:07:39 +08:00
Sean Cross 4b6ad47705 sw: spi-gpio: remove unused files
These were moved into spi.c to allow them to be inlined.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 11:04:43 +08:00
Sean Cross 91a23d808c sw: Makefile: fix third_party and package naming
With these changes, litex mode should now work.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 10:51:08 +08:00
Sean Cross 2a7e431947 hw: foboot-bitstream: support building bios
Now that the failsafe bios has stabilized, support building it as part
of the ROM.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 09:42:29 +08:00
Sean Cross a9a5f0cc0d sw: minor removal of unused code
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 09:35:50 +08:00
Sean Cross d6ae51f69b sw: ld: add support for litex build
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 09:35:23 +08:00
Sean Cross 2437e8b5c1 sw: Makefile: support litex build environment
Allow this software package to be built from within the Litex build system.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-06 09:34:26 +08:00
Sean Cross 6435aec3db foboot-bitstream: add hacker revision
Untested commit -- this should add support for the Hacker version of the
PCB.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 18:58:19 +08:00
Sean Cross b40a9bad43 rgb: fix typo in macro
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 18:17:11 +08:00
Sean Cross b5f17acdf6 sw: enable fancy led effects
Support different LED patterns depending on the mode.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 18:10:05 +08:00
Sean Cross db65ccc199 hw: document warmboot some more
Realized that there are 5 images and not 4.  With this, everything
works as it should.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 17:34:10 +08:00
Sean Cross fd76536351 usb-dev: reboot to image 0
Since this is a failsafe bootloader, reboot to Image 0.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 17:33:28 +08:00
Sean Cross eaa0d63ccb usb-epfifo: fix DATA0/DATA1 swaps for Tx
We were sometimes sending DATA0 instead of DATA1.  Fix that.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 17:33:02 +08:00
Sean Cross 7d091a72b9 main: poll dfu in main loop
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 15:10:19 +08:00
Sean Cross 7bcc8529d6 dfu: initial working commit
This commit actually works, but still has a few issues.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 15:09:52 +08:00
Sean Cross ac349ce1a1 spi: support 32- and 64-kB erase blocks
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 15:09:04 +08:00
Sean Cross cbf4958236 usb-epfifo: remove extra USB_NAK setting
This works around an issue, though it's still not properly working.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 13:21:09 +08:00
Sean Cross 2b0575889f usb-dev: remove commented-out printf statements
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 13:20:54 +08:00
Sean Cross c92af747cd spi: actually transmit data
We were sending data on CS, not on CLK.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-05 13:19:46 +08:00
Sean Cross d72925fdeb sw: experimental improved dfu performance
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-03 22:11:31 +08:00
Sean Cross 4c3f0f2402 valentyusb: use experimental shorter pipeline
This helps to improve timing.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-03 22:11:02 +08:00
Sean Cross 1660681d38 hw: add 2-stage-1024-cache
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-03 21:57:53 +08:00
Sean Cross c33d86adb9 foboot-bitstream: fix warmboot and add rgb block
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 18:11:58 +08:00
Sean Cross 85b6d75882 sw: update to latest generated files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 18:11:25 +08:00
Sean Cross dbc0f81715 usb: re-enable extra usb descriptors
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 18:11:09 +08:00
Sean Cross 851e54e70c main: initialize rgb driver
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 18:10:56 +08:00
Sean Cross 85bdc7991c rgb: add initial rgb code
It initializes the rgb driver, and can write to its registers.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 18:10:17 +08:00
Sean Cross a3c05bd19e sw: system: add reset key to reboot()
Add the key `0xac` to the reset command, to prevent accidental resets.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 16:40:09 +08:00
Sean Cross 33aec0876b sw: usb-epfifo: disable usb events before disconnect
If we don't disconnect the usb event system, then we will lock up
in an irq storm.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 16:39:13 +08:00
Sean Cross 8599ec7007 hw: bitstream: simplify command line argument parsing
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-02 13:03:47 +08:00
Sean Cross 0e720d5acc README: add information about sw and hw and building
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-01 10:10:13 +08:00
Sean Cross 2fd01b8303 foboot-bitstream: more help description, add dvt support
Signed-off-by: Sean Cross <sean@xobs.io>
2019-04-01 10:09:20 +08:00
Sean Cross 0a4c73a63a sw: wip commit -- dfu state machine works, spi broken
Need to fix SPI and SB_WARMBOOT, but the DFU state machine now works.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-28 11:13:25 +08:00
Sean Cross 6595eb1ef1 valentyusb: increase incoming buffer to 128 bytes
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-28 11:12:32 +08:00
Sean Cross 7191c12490 wip: just need to get WARMBOOT working
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-28 11:11:36 +08:00
Sean Cross a3298226e5 sw: getting dfu working about to add debug bridge
We're having issues getteing DFU working.  It almost works, but has some
issues.  Unfortunately, we're out of space for printf.

Now we will work to get the UART Wishbone bridge working, to move
forward on debugging support.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-26 09:39:55 +08:00
Sean Cross b0afd2b0ae sw: puts: don't eat first character
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-25 18:59:57 +08:00
Sean Cross 3d6acaf51e sw: wip commit -- getting dfu working
Now that we have SPI and USB both working, we can start to close the
loop and get DFU working.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-25 17:39:06 +08:00
Sean Cross 7210ee219d sw: add div/mul instructions
For foboot, we're using a CPU without a divide or multiply instruction,
to save gates.  Replace these with software implementations.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-21 11:01:46 +08:00
Sean Cross 2139317530 sw: include: sync csr.h and mem.h
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 13:14:57 +08:00
Sean Cross dd9cdfa7a3 sw: usb-epfifo: make buffer pointers static
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 13:14:20 +08:00
Sean Cross 23b9962067 hw: foboot-bitstream: remove pmod debug comments
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 13:12:53 +08:00
Sean Cross fa690d63ed hw: foboot-bitstream: clean up debug generation
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 13:12:29 +08:00
Sean Cross 4f0507fc77 hw: foboot-bitstream: remove "generating firmware" message
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 13:12:08 +08:00
Sean Cross f3d779787b hw: foboot-bitstream: add reset to usb_48
This is required to meet timing.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 13:11:45 +08:00
Sean Cross b09333f023 hw: add spi and new vexriscv to foboot
This is the beginning of having SPI.

Also add a new two-stage pipeline.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 11:25:09 +08:00
Sean Cross 03327067ff sw: add spi base
This is taken from fomu-flash, and needs to be adapted.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-20 11:23:19 +08:00
Sean Cross c7632ae8bd deps: litex: sync with latest version
This pulls in several fixes, including custom vexriscv modules.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-17 17:33:31 +01:00
Sean Cross 4aa3861c03 hw: deps: update to first feature-complete valentyusb
This is the first version of `valentyusb` that successfully enumerates
without any errors.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-11 11:52:03 +08:00
Sean Cross ff4774ebc3 sw: usb-epfifo: fixups to remove errors in enumeration
With these fixups, we can now enumerate successfully.

The code is a mess, but this is the first functionally-correct build.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-11 11:51:19 +08:00
Sean Cross 9909b3bbdb sw: usb-desc: set product name to "Fomu Bootloader"
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-11 11:50:55 +08:00
Sean Cross f5fd282d61 sw: usb-dev: remove errant `i++`
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 22:46:02 +08:00
Sean Cross 5bcd6c44fb deps: update valentyusb to working rev
This revision works, although more tuning needs to be done.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 22:43:28 +08:00
Sean Cross d64f88d995 sw: usb-epfifo: mostly-working commit
Still has issues with large reads.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 22:42:14 +08:00
Sean Cross c180c1b1b8 sw: usb-epfifo: increase packet size to 64 bytes
This masks some problems we're seeing.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 22:41:22 +08:00
Sean Cross a552d1eb91 usb-dev: limit the size of outgoing packets
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 22:40:52 +08:00
Sean Cross 2d7c7794f5 hw: foboot-bitstream: remove debug pins, use epfifo
Remove the debug pins to let timing close.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 21:06:37 +08:00
Sean Cross 0c6e444789 hw: foboot-bitstream: add -relut and friends to nextpnr
Shrink the resulting gate count by adding -relut and adjusting the
number of luts that a CE signal can use.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 21:05:51 +08:00
Sean Cross 8aed600cd6 hw: foboot-bitstream: specify additional clock domain constraints
Specify all the clock domain constraints for every possible signal, to
work around the fact that nextpnr currently will pick one and ignore the
rest.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 21:05:12 +08:00
Sean Cross 6638801886 hw: foboot-bitstream: remove clk48_in signal
It's unused.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 21:04:19 +08:00
Sean Cross 8fb6b5977b hw: foboot-bitstream: remove unused clk48 net
We only use the raw and usb48 nets.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 20:53:58 +08:00
Sean Cross 69f7b5d836 csr: replace with latest generated version
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 20:53:30 +08:00
Sean Cross c98017cbc9 client: working on the client
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-10 15:25:33 +08:00
Sean Cross 13360015db Merge branch 'master' of github.com:xobs/foboot 2019-03-08 20:49:13 +08:00
Sean Cross d603113b6f foboot-bitstream: send clk48 through shifter, then through pll
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-08 20:47:42 +08:00
Sean Cross 44ee19c8b4 valentyusb: use latest fix for metastable transmissions
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-06 14:06:17 +08:00
Sean Cross f34601df98 hw: lxbuildenv: fix uninitialized repo issue
We would get stuck in a loop.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 22:08:11 +08:00
Sean Cross 3df59a866d metastable fix: wip
Trying to figure out what's causing this problem.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 20:28:54 +08:00
Sean Cross c0842737bf sw: usb-unififo: add code to test usb RX
This just reads SOF packets and validates they're correct.  This ensures
that bitstuffing is good.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 15:50:36 +08:00
Sean Cross ce071dac67 sw: main: remove usb_sync() call
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 15:50:08 +08:00
Sean Cross 7f30d7f79b sw: Makefile: hardcode project name to "foboot"
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 15:49:21 +08:00
Sean Cross 380a4f1fa2 sw: unififo: print out SOF frames, to ensure link integrity
Print out the SOF frames so that we can make sure the Rx path is good.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 14:52:42 +08:00
Sean Cross 73176b65de hw: lxbuildenv: fix detection of .git directory
It was giving an incorrect path, which would cause it to refresh
submodules during every build.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 13:20:54 +08:00
Sean Cross c78e9ab214 sw: add missing include files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 12:02:02 +08:00
Sean Cross ffd0285613 main: more work on fixing stuff
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 11:54:48 +08:00
Sean Cross 1c38c58a6f Merge branch 'master' of git.xobs.io:xobs/foboot 2019-03-05 09:18:20 +08:00
Sean Cross 350497924e README: add simple readme file
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 09:17:20 +08:00
Sean Cross 74ec6be245 hw: remove gitignore
It's stored in the root now

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 09:16:52 +08:00
Sean Cross 1c8634e954 gitmodules: add hw deps
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 09:16:36 +08:00
Sean Cross 8fe27d9371 Add 'hw/' from commit 'd812378c4d61f7c957ac4bcba15a8344fb7fb458'
git-subtree-dir: hw
git-subtree-mainline: e4af98b4aa
git-subtree-split: d812378c4d
2019-03-05 09:05:50 +08:00
Sean Cross e4af98b4aa generated: update generated csr, mem, and ld files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 09:05:29 +08:00
Sean Cross d812378c4d deps: update valentyusb
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 08:56:30 +08:00
Sean Cross 84d4b40897 foboot: move software stuff to sw directory
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-05 08:54:51 +08:00
Sean Cross b06cd3b807 usb-epfifo: something is actually responding now
Making good progress. Still not reliable, but there's something to work on now.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-04 15:46:59 +08:00
Sean Cross d18e0cba0d include: regenerate included headers
Signed-off-by: Sean Cross <sean@xobs.io>
2019-03-04 15:46:47 +08:00
Sean Cross a897c7f2fa foboot-bitstream: remove `cas` module from imports
It's unused in the current implementation.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-28 10:52:06 +08:00
Sean Cross 6a147c4333 valentyusb: pull latest version
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-28 10:51:35 +08:00
Sean Cross 2ac79e45e9 Makefile: don't re-set GIT_VERSION
Use := to only set GIT_VERSION once.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-28 10:49:02 +08:00
Sean Cross 1048cbf999 third_party: minor formatting cleanups
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-28 10:48:47 +08:00
Sean Cross fae65117cd usb: work-in-progress for USB development
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-28 10:48:25 +08:00
Sean Cross 1cb67f8f8d crc5-test: add simple program to generate SOF frames
This can be used to tell if we're properly decoding packets.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-28 10:45:37 +08:00
Sean Cross cad2ae01d7 foboot-bitstream: use crystal for 12 MHz clock
This fixes heterodyning that was occurring in the USB block, as it
transitioned from the 48 MHz down to the 12 MHz domain.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-27 14:20:04 +08:00
Sean Cross dd7839cc68 deps: migen: fix upstream ref
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:36:21 +08:00
Sean Cross 05480670c5 migen: update submodule
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:32:48 +08:00
Sean Cross 6ca4b6a398 deps: valentyusb: update submodule
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:32:28 +08:00
Sean Cross 5fbd0fc667 lxbuildenv: riscv: also allow riscv32 toolchain
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:25:16 +08:00
Sean Cross d42418d62a foboot-bitstream: mark executable
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:22:05 +08:00
Sean Cross 7caff80f6b foboot-bitstream: change line endings
These matter on non-Windows machines.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:21:23 +08:00
Sean Cross 6771d28fb4 foboot-bitstream: work-in-progress commit
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:19:17 +08:00
Sean Cross 8b54d5addb csr-test: add simple program to test CSRs
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 16:19:01 +08:00
Sean Cross 44880465e6 Merge branch 'master' of git.xobs.io:xobs/foboot-bitstream 2019-02-25 15:16:57 +08:00
Sean Cross 49b22a1962 foboot-bitstream: use new-style random rom
Use a new pattern with a new function to generate the random ROM, used
for ROM patching.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 15:16:19 +08:00
Sean Cross e66f909bfd lxbuildenv: add '-r' command to run modules under lxbuildenv
This will allow us to e.g. run tests.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 13:54:26 +08:00
Sean Cross 30d5a097d2 gitignore: ignore env, build, pycache
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 12:44:19 +08:00
Sean Cross 21cdcaaee8 foboot-bitstream: first full build
This actually compiles now, hooray!  But does it work? No.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 12:43:29 +08:00
Sean Cross ee006eff90 deps: add valentyusb
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 12:43:09 +08:00
Sean Cross 245a466654 lxsocsupport: ignore pycache files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 12:42:06 +08:00
Sean Cross ea7e99af5b deps: change lx-socsupport to lxsocsupport
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 12:10:15 +08:00
Sean Cross 7f77c49c7b lx-socsupport: add submodule
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 11:06:35 +08:00
Sean Cross f9eeee3ff7 lxbuildenv: fix generation of initial python
It still doesn't work (owing to some bit rot), but it's better now.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 10:48:57 +08:00
Sean Cross 68694294e1 initial commit
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-18 10:48:43 +08:00
Sean Cross 5f0d83b2e2 wip: packets kinda flow now
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-25 14:28:03 +13:00
Sean Cross d31bcea114 third-party: copy data section at boot
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-25 14:27:43 +13:00
Sean Cross c447457ffc about to break things
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-23 16:52:47 +13:00
Sean Cross 167201228f wip: generate a buildable image
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-23 15:25:07 +13:00
Sean Cross 9a14f6d08d usb: add stub usb file
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-22 14:02:15 +13:00
Sean Cross d6403cc3ec input: add bios binary files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-22 14:02:04 +13:00
Sean Cross 7647b4a620 foboot: use endpoint-based csr files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-22 14:01:46 +13:00
Sean Cross 4b4b993075 add input files
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-22 13:27:08 +13:00
Sean Cross 686640f2e0 remove usb and fix startup
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-22 12:30:52 +13:00
Sean Cross 9295c3d6cc makefile: remove C++ and DFU
These aren't so useful right now.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-22 12:30:22 +13:00
Sean Cross 249c289aa7 grainuum: add support libraries
Add supporting files for grainuum.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-01 23:31:51 +08:00
Sean Cross 397d153a44 grainuum: initial commit
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-01 23:03:45 +08:00
Sean Cross b31c0213b5 usb: add empty usb implementation
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-01 22:37:14 +08:00
Sean Cross ebed2b919f qsort: fix warnings
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-01 22:13:59 +08:00
89 changed files with 30808 additions and 778 deletions

11
.gitignore vendored
View File

@ -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

39
.gitmodules vendored Normal file
View File

@ -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
View File

@ -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)

View File

@ -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

View File

@ -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

3394
hw/2-stage-1024-cache.v Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
iBus: !!vexriscv.BusReport
flushInstructions: [16399, 19, 19, 19]
info: !!vexriscv.CacheReport {bytePerLine: 32, size: 1024}
kind: cached

3769
hw/2-stage-no-cache-debug.v Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

4099
hw/4-stage-no-cache-debug.v Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1 @@
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 4}

14
hw/bin/litex_read_verilog Executable file
View File

@ -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()

14
hw/bin/litex_server Executable file
View File

@ -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()

14
hw/bin/litex_sim Executable file
View File

@ -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()

14
hw/bin/litex_simple Executable file
View File

@ -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()

14
hw/bin/litex_term Executable file
View File

@ -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()

14
hw/bin/mkmscimg Executable file
View File

@ -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()

168
hw/crc5-test.c Normal file
View File

@ -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;
}

76
hw/csr-test.py Normal file
View File

@ -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()

1
hw/deps/litedram Submodule

@ -0,0 +1 @@
Subproject commit fd3e9afbcd0872b645badc9b75aacab6dbf7459e

1
hw/deps/liteeth Submodule

@ -0,0 +1 @@
Subproject commit 77fa4bfb1e452adb1fa34c1b0baede68c056763d

1
hw/deps/liteiclink Submodule

@ -0,0 +1 @@
Subproject commit ee9ae87641d98a1b9ad88e28042394797bfb9e00

1
hw/deps/litepcie Submodule

@ -0,0 +1 @@
Subproject commit 3804c4947adedc6c720e3041e518627b0bf57f78

1
hw/deps/litescope Submodule

@ -0,0 +1 @@
Subproject commit c1d8bdf6f23b1070b8bd2dd277a4708863474148

1
hw/deps/litesdcard Submodule

@ -0,0 +1 @@
Subproject commit c07165ff9f9e368c8147158b88fe90cea683e51c

1
hw/deps/liteusb Submodule

@ -0,0 +1 @@
Subproject commit 0a9110f901182a1233cc4e64b6e39175f6784621

1
hw/deps/litevideo Submodule

@ -0,0 +1 @@
Subproject commit 98e145fba8c25394e9958bad67e2a457d145127e

1
hw/deps/litex Submodule

@ -0,0 +1 @@
Subproject commit fd7ed6c1ec24ffbdbebb465bc8cc4713b6d40181

1
hw/deps/lxsocsupport Submodule

@ -0,0 +1 @@
Subproject commit 74f133340144630e000f91e466ac5107f87a8d5a

1
hw/deps/migen Submodule

@ -0,0 +1 @@
Subproject commit ae421054f1e3621621bcc397df171caf3681763c

1
hw/deps/pyserial Submodule

@ -0,0 +1 @@
Subproject commit a4d8f27bf636ee598c4368d7e488f78a226bf778

1
hw/deps/valentyusb Submodule

@ -0,0 +1 @@
Subproject commit 65292f0adbe688bda27f0b202cba9bea28de0d14

744
hw/foboot-bitstream.py Executable file
View File

@ -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()

549
hw/lxbuildenv.py Normal file
View File

@ -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)

View File

@ -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

View File

@ -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;
}

122
sw/Makefile Normal file
View File

@ -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)

83
sw/include/dfu.h Normal file
View File

@ -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 */

416
sw/include/generated/csr.h Normal file
View File

@ -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

View File

@ -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

602
sw/include/grainuum.h Normal file
View File

@ -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 */

10
sw/include/rgb.h Normal file
View File

@ -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_ */

93
sw/include/spi.h Normal file
View File

@ -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_ */

View File

@ -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 */

15
sw/include/time.h Normal file
View File

@ -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 */

109
sw/include/toboot-api.h Normal file
View File

@ -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_ */

View File

@ -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_ */

95
sw/include/usb-desc.h Normal file
View File

@ -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

29
sw/include/usb.h Normal file
View File

@ -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

55
sw/include/webusb-defs.h Normal file
View File

@ -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

912
sw/include/xxhash.c Normal file
View File

@ -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 */

294
sw/include/xxhash.h Normal file
View File

@ -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 */

View File

@ -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

View File

@ -1 +1 @@
OUTPUT_FORMAT("elf32-littleriscv")
OUTPUT_FORMAT("elf32-littleriscv")

View File

@ -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
}

278
sw/src/dfu.c Normal file
View File

@ -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;
}

102
sw/src/main.c Normal file
View File

@ -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;
}

113
sw/src/rgb.c Normal file
View File

@ -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
}

948
sw/src/spi.c Normal file
View File

@ -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;
}

140
sw/src/toboot.c Normal file
View File

@ -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,
};
*/

229
sw/src/usb-desc.c Normal file
View File

@ -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}
};

271
sw/src/usb-dev.c Normal file
View File

@ -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;
}

257
sw/src/usb-epfifo.c Normal file
View File

@ -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 */

225
sw/src/usb-unififo.c Normal file
View File

@ -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 */

121
sw/third_party/div.S vendored Normal file
View File

@ -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

View File

@ -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

117
sw/third_party/libbase/libc.c vendored Normal file
View File

@ -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;
}

View File

@ -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;

View File

@ -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

26
sw/third_party/mul.S vendored Normal file
View File

@ -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

268
sw/third_party/printf.c vendored Normal file
View File

@ -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;
}

245
third_party/printf.c vendored
View File

@ -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);
}