X-Scale has quit [Quit: HydraIRC -> http://www.hydrairc.com <- It'll be on slashdot one day...]
Bike has joined ##openfpga
pie_ has joined ##openfpga
azonenberg_work has joined ##openfpga
LeelooMinai has joined ##openfpga
digshadow has quit [Quit: Leaving.]
LeelooMinai has left ##openfpga [##openfpga]
amclain has joined ##openfpga
azonenberg_work has quit [Ping timeout: 260 seconds]
hobbes`` has joined ##openfpga
qu1j0t3 has quit [*.net *.split]
dingbat has quit [*.net *.split]
promach has quit [*.net *.split]
hobbes- has quit [*.net *.split]
hobbes`` is now known as hobbes-
qu1j0t3 has joined ##openfpga
dingbat has joined ##openfpga
digshadow has joined ##openfpga
azonenberg_work has joined ##openfpga
promach has joined ##openfpga
digshadow has quit [Quit: Leaving.]
<rqou>
(playing with Rust) wow, bindgen is pretty good and can handle basic C++
digshadow has joined ##openfpga
<ZipCPU>
rqou: You asked about a bare bones SPI. My big problem with that is that every peripheral controller I've come across seems to use the chip select in a different fashion.
<ZipCPU>
Still, I have a bare bones QSPI flash reader, if you are interested in that.
<rqou>
i don't care about chip select because i always have to end up manually controlling it
<ZipCPU>
I've also got a bare bones lower-level SPI protocol runner ... so if you only want the lower level stuff ... yeah, I've got a bare bones one of those.
<rqou>
hmm, rust bindgen seems to have trouble with nested namespace definitions
<rqou>
which clang tells me "nested namespace definition is a C++1z extension"
<qu1j0t3>
of course i stole it from a conversation whitequark is havnig on twitter
<whitequark>
azonenberg_work: is there anything like USB-DFU but for TCP?
<whitequark>
or really anyone:
<azonenberg_work>
whitequark: TFTP is the closest i'm aware of
<whitequark>
ugh
<azonenberg_work>
that's what e.g. cisco router recovery modes use
<azonenberg_work>
i do not believe there's a firmware update standard for ethernet based protocols like there is for usb
<whitequark>
>TFTP defines three modes of transfer: netascii, octet, and mail.
<whitequark>
>mail
<whitequark>
what
<azonenberg_work>
octet is basically the only one anyone uses anymore
<azonenberg_work>
at leasti n my experience
<azonenberg_work>
b/c nobody uses tftp except for PXE and remote firmware updates
<azonenberg_work>
If i were to implement a LAN-reflashable device i'd likely use TFTP to push a signed image file over
<azonenberg_work>
have the device cache locally, verify the sig, then burn it if it's good
<whitequark>
I don't have enough RAM to store the entire firmware
<whitequark>
for one
<azonenberg_work>
Flash?
<whitequark>
well
<whitequark>
I could hack around it, sure
<azonenberg_work>
you could write to the high half of flash then if the checksum is good copy to the low half
<azonenberg_work>
or something like that
<azonenberg_work>
or, as a last resort
<azonenberg_work>
bulk erase flash, burn the image, checksum
<whitequark>
that means that a reset in the middle leaves the device unbootable
<azonenberg_work>
if the checksum fails repeat the download and just hope it's successful before the nextp ower cycle
<azonenberg_work>
correct
<whitequark>
at least not over the network
<whitequark>
that's gross
<azonenberg_work>
that happens no matter what your updaet alg is if you don't have enough memory for two copies
<azonenberg_work>
If you have enough flash for two copies, you can have a fallback
<whitequark>
well, no
<azonenberg_work>
xilinx fpgas have boot fallback in hardware already
<azonenberg_work>
with an mcu you can do it in a bootloader
<whitequark>
I could have a bootloader
<azonenberg_work>
it could be as simple as, check a certain address to see if it's 0xff
<whitequark>
I wonder how large smoltcp would be in a minimal configuration
<azonenberg_work>
if not, jump to it
<azonenberg_work>
otherwise jump to a diff address that has a recovery image
<lain>
in the past I've written a bootloader (dedicated bootloader region in flash) that overwrites current firmware with new firmware, but only sets the valid bit in flash if the checksum matches or etc
<azonenberg_work>
lain: the issue is to avoid bricking the device on a failed flash
<azonenberg_work>
the only viable option is to have a fallback firmware
<lain>
azonenberg_work: it wouldn't be bricked, the bootloader is always present
<lain>
the bootloader never erases itself, it just erases everything else
<azonenberg_work>
So then the bootloader is your fallback firmware
<azonenberg_work>
as long as it still implements an update algorithm
<lain>
but this was on a chip with a dedicated bootloader region and a flash erase command that, allegedly, can't erase the bootloader :P
<lain>
right, the entire update process is implemented in the bootloader in my suggestion
<azonenberg_work>
sure, that works nicely if you have it
<lain>
and then if I need to save space I just call through the bootloader to reuse routines where possible :P
<lain>
but yeah the A/B firmware route is another way to go, just have two full firmware regions and swap the pointer when the new one is validated
<lain>
that also lets you trivially set a "first boot of new firmware" flag and if the boot process doesn't set another bit within some watchdog timeout, you can fall back
<whitequark>
this chip actually has some provisions for that
<lain>
it's when you have to patch the bootloader that things get really scary :D
<azonenberg_work>
lain: that s how xilinx fpga multiboot works
<azonenberg_work>
except the "bootloader" is hardware and cant be patched
<lain>
azonenberg_work: ah yeah
<azonenberg_work>
its just a state machine that boots from one location then if that fails tries the other
<whitequark>
lain: hm, that's a good point
<whitequark>
if you have a robust process for doing the A/B update process
<whitequark>
you are never stuck with an outdated or broken bootloader
<azonenberg_work>
Yep
<azonenberg_work>
Your "bootloader" can be a single jmp instruction
<azonenberg_work>
that you patch after verifying a good update :p
<lain>
yeah
<azonenberg_work>
Although you probably want the fallback etc in the bootloader too but it can still be made super simple
<azonenberg_work>
and need not actually implement the update algorithm itself
<lain>
and if you don't have a separate bootloader, you can implement the "new firmware didn't boot" failsafe by jumping to new firmware live without patching flash, then make the new firmware write the new jmp instruction to flash after a successful boot, or etc
<lain>
I often see that initial jmp referred to as a trampoline :3
<whitequark>
you don't need a jmp on cortex-m even
* azonenberg_work
bounces on lain's trampoline
<whitequark>
just reprogram the start pc(/sp)
<lain>
haha
<whitequark>
lol azonenberg_work
<azonenberg_work>
whitequark: yeah you could just rewrite teh vector table
<azonenberg_work>
but you'd also have to patch all of the other offsets for ISRs
<azonenberg_work>
the other thing i see done is, you have the intiai lvector table jump to a trampoline
<whitequark>
what do you mean?
<azonenberg_work>
which then jumps to a stub in the A or B image
<azonenberg_work>
which loads a new vector table specific to that image
<azonenberg_work>
whitequark: if you dont have an MMU
<azonenberg_work>
the A and B images will be at different addresses
<azonenberg_work>
So unless your ISRs are shared by both images
<azonenberg_work>
your vector table has to change
<azonenberg_work>
you cant just reprogram the start pc/sp in the vector table, you have to rebuild the whole thing
<whitequark>
sure you can
<azonenberg_work>
i mean you can reprogram those offsets
<azonenberg_work>
but then your ISR addresses dont change
<whitequark>
just make the firmware set the vector table offset to its own vector table via a pc-relative instruction
<azonenberg_work>
Yeah but you need an initial vector table at boot
<azonenberg_work>
used by the rom bootloader etc
<whitequark>
rom bootloader?
<azonenberg_work>
or whaetver state machine in the cpu loads the initial vector table from the start of flash
<azonenberg_work>
What i'm saying is, you have one fixed vector table at the start of flash
<azonenberg_work>
which you patch to point to image a or b
<azonenberg_work>
then those images have their own vector tables
<whitequark>
yes, I reprogram pc/sp at offset 0, how's that not enough?
<whitequark>
I don't understand what are you referring to
<azonenberg_work>
What i meant was, ypi cpi;dm
<azonenberg_work>
what i meant was
<azonenberg_work>
you couldn't just have one vector table at the start of flash and have that be it
<azonenberg_work>
you need a table in each image that you set vtor to
<whitequark>
oh yeah sure, why would I have one? that's not even convenient
<azonenberg_work>
plus one at the start of flash whose sole purpose is to get patched to point to one image or the other
<whitequark>
what if A/B firmwares have different sets of vectors
<azonenberg_work>
exactly
<whitequark>
ah wait I think I see what you mean
<whitequark>
I can't reuse the reset vector pc/sp as the A firmware vector table
<whitequark>
s/as/in/
<whitequark>
yeah that's mildly annoying
<lain>
sacrifice an entire flash erase sector just for your trampoline :V
<azonenberg_work>
Weeeellll...
<azonenberg_work>
There's some tricks you can do
<whitequark>
16KB erase sectors on this chip... damn
<whitequark>
I mean it's still 1MB of flash, but
<lain>
yeesh
<azonenberg_work>
Some (not all) flash
<azonenberg_work>
allows you to do multiple writes to the same addresses
<lain>
yeah
<azonenberg_work>
and the end resulti s basically the bitwise and of all writes
<azonenberg_work>
So you could put one image at say 0x48000 and another at 0x40000
<lain>
isn't it the bitwise OR?
<azonenberg_work>
1 -> 0
<azonenberg_work>
is the only legal write
<lain>
ah right flash is programmed backwards
<azonenberg_work>
(unless the flash inverts somewhere)
<azonenberg_work>
Anyway, iff your flash allows this
<azonenberg_work>
you could clear one bit in the vector table
<lain>
yeah, I've used this to implement eeprom emulation on flash to reduce wear
<whitequark>
my flash allows this I thin
<azonenberg_work>
anyway so what you could do is, have one image start say 16KB after the midpoint of flash
<azonenberg_work>
have that be your A image
<azonenberg_work>
Second image starts 16KB after the start of flash, that's B
<azonenberg_work>
Vector initially points to A
<azonenberg_work>
To do an update, you write the new image to the B address
<azonenberg_work>
Verify it
<azonenberg_work>
then poke the one bit in the pointer
<azonenberg_work>
now you're good to go
<azonenberg_work>
at some point in the future, before starting another update
<azonenberg_work>
you lazily copy the good image from B back to A
<azonenberg_work>
then wipe the first sector and rewrite pointing to A
<azonenberg_work>
that gives you a small window of a few ms that you could brick on power outage
<azonenberg_work>
but makes the main update process atomic
<lain>
and if you can characterize that brickable time period you can ensure there's sufficient capacitance to float through it
<azonenberg_work>
Correct
<azonenberg_work>
it's basically the time to erase one flash sector and rewrite the first few words
<azonenberg_work>
Since the remainder of the initial sector is A's vector table
<azonenberg_work>
and the initial pc points to B
<azonenberg_work>
corruption elsewhere in the table will not brick
<azonenberg_work>
so really you have to do an erase and 64-bit write in the brick window
<lain>
hm, actually I guess you would have to use a BOD to abort that if it's already low or something now that I think about it... since the power could cut at any point prior to the brick window, with the cap discharge starting /before/ the brick window
<azonenberg_work>
Correct
<whitequark>
this flash core actually has an integrated BOD
<lain>
but that could be as simple as verifying the voltage is sufficiently high (no power loss) just prior to doing it
<lain>
oh neat
<azonenberg_work>
you'd have to watch vcc and make sure you were good just before starting
<azonenberg_work>
then do the atomic operation
<azonenberg_work>
but the primary firmware update process need not be fast since the pointer swap *is* atomic
<azonenberg_work>
and even a failure halfway through the update that leaves that bit in an undefined state
<azonenberg_work>
will result in one of the two images executing :p
azonenberg_work has quit [Ping timeout: 258 seconds]
ZipCPU|Laptop has quit [Ping timeout: 260 seconds]