What does this firmware do?
Static tools read an image. flowRS has twins of its boards, so it can also run an unknown image and watch what it does, then do something to it and see what changes.
It is for firmware you own or are authorised to examine. It runs on a simulated twin only and never touches hardware. In the IDE it runs in your browser, on the twin the panel holds: your firmware does not go to any server. From flowrs-mcp or flowrsc it runs on your own machine.
Where to find it
- The IDE: Hardware, the Digital Twin panel, "What does this firmware do?". Load a
.hex,.uf2,.binor.elfabove it, or use a built-in program. - An agent: the MCP tool
firmware_explain. A hosted workspace is not offered it. - A terminal:
flowrsc firmware explain IMAGE --board BOARD, in a build with the twins (cargo build -p flowrs-cli --features firmexplain). The public engine has no twins, and says so.
--board takes a board's id (bluepill_f103c8) or a twin platform (stm32f4). With no board, it ranks the platforms the image could be for.
What comes back
Three parts that do not mix.
Observations: what this run measured, on which twin, at what board time.
- peripherals touched, named from the register database, with first access and counts, and on request every register with the fields it was left holding;
- the clock tree the firmware configured, from the clock registers and the line's reference manual, each frequency with its working. A clock from a crystal is stated as a multiple of it until you say what the crystal is: that is the board's, not in any register;
- pins: frequency and duty off the sampled wave, how each was configured, and the level changes the firmware's own stores made, exact where the sampling is not;
- what went out of the serial ports, and the bit rate their dividers set;
- I2C and SPI traffic, with addresses and transactions: from each controller's registers, then through the stacked
registersdecoder, since the twins' models carry bytes with no level on any pin; a bus the firmware drives itself with GPIO stores goes through the bit-level decoders; - timers and PWM as they were left, NVIC interrupts turned on, and what changed in flash.
An observation never states a purpose. "Toggles PC13 at 50 Hz" is an observation; "this is a blink" is an interpretation.
Evidence: enough to run it again and get the same trace. The image's SHA-256, the board, the twin's engine, the budget, the stimuli, the digest of every recorded access, the pins and the serial output, and the command that does it.
Unknowns: what this run does not say. A run shows what this run did, not all the firmware could do. Each report says which code the program counter was never seen in, which inputs it read that nobody gave, what the twin has no model for, what the register database could not judge, an access to no register (a likely wrong board, or a block this twin does not model), and what its family's twin cannot report.
Interpretations are kept apart. flowRS adds none. One an agent or a person adds is labelled as an interpretation, with who said it and the observations it rests on, and it cannot rest on nothing.
Do something to it, and compare
PA0=1 hold PA0 high from reset
PA0=1@5ms the same, from 5 ms in
PA0=0@5ms+20ms low at 5 ms, back after 20 ms: a button
uart:hello\n feed bytes to the first serial port, at reset
uart2:AT\r\n@10ms to the second, at 10 ms
i2c1@0x50 put an echo device on I2C1 at address 0x50, before the run
spi1@flash put a flash chip (a W25Q128) on SPI1, before the run
A run takes at most 32 stimuli and 4,096 bytes fed to its serial ports, and its board time is bounded too (run::MAX_CYCLES in the crate); it ends early at its wall-clock limit, or when the CPU locks up or leaves the image's code, and says which.
Compare two runs and it says what changed: the first trace entry where they part, with the instruction that made each run's access there, the peripherals only one touched, what only one printed, the pins that moved differently, the access sites only one reached. That is how a code path is found. It does not say which run is the normal one.
Which board is this for?
The image rules some out with no run: a UF2 declares a family, an ELF its machine, a Cortex-M vector table holds a stack pointer and a reset vector that have to be in the part's map. Each platform left runs for a short bounded time and is scored on what a wrong board shows (accesses no register answers, addresses no model claims, the checker's errors, getting nowhere) and what a right one shows (distinct access sites, output).
Parts of one line share their maps, so what a firmware touches often does not tell them apart. Then the answer is the tie, and a run that shows nothing is "cannot tell". Boards whose LED is on a pin the image drove are offered as something to check, not a finding.
On every fixture flowRS holds with a known board, the right platform is in the answer: alone where nothing else on its line runs the image alike, and otherwise in a tie on its own line. The one tie that leaves its line is an L4 blink's, which touches only what an L4 and a G4 have the same, at the same addresses. GigaDevice made its GD32F10x, F30x and F403 register-compatible with the STM32F1 and its GD32F4xx with the STM32F2 and F4, so an image for one runs on the other at the same addresses: those ties hold both, and say they cannot tell them apart. A GD32 twin counts its part's rated clock while its CPU starts at 8 MHz (16 MHz on the F4xx), so its bounded run is longer in board time by that ratio.
What each family reports
| Family | Runs in | Reports | Cannot report |
|---|---|---|---|
| STM32 F1, F2, F4, F0, G0, H7, L5 | panel, MCP, CLI | everything above | PWM on pins (reported from its registers), wire-level buses |
| STM32 F7, L0, G4, L4 | panel, MCP, CLI | the same, block names from a table of the reference manual's | any judgment against a register file (the database has none for the line), register and field names |
| nRF52, nRF5340 | panel, MCP, CLI | everything above; EasyDMA's bytes from RAM | the radio's air, wire-level buses |
| SAM D21, D51 | panel, MCP, CLI | everything above; the CPU clock is the twin's GCLK model's answer | a clock formula from the registers, wire-level buses |
| RP2350 | panel, MCP, CLI | everything above | what the boot ROM's own code did beyond its accesses, wire-level buses |
| Silicon Labs EFR32 Series 2 | panel, MCP, CLI | everything above, with its blocks named from Silicon Labs' device headers, as the register database has no file for these parts; the clock tree from the CMU and the DPLL, each line's own CLKSEL codes, with a HFXO clock a multiple of the crystal unless given; each block's secure and non-secure addresses and its set, clear and toggle windows read as the store they are | which pin a peripheral's signal reaches (its route is the data sheet's, not read here); a band of the HFRCO the firmware chose, whose frequency is in the part's calibration table and not its register, so a clock from one is left unworked; the blocks no model runs (the radio, IADC0, LETIMER0, LDMA, the Secure Element and the rest the twin's notices name), whose stores are reported and not run; PWM on pins; wire-level I2C, SPI or UART |
| Silicon Labs EFR32 Series 1, EFM32 Series 0 | panel, MCP, CLI | everything above, from each part's own register file (Silicon Labs' SVDs); the clock tree from the CMU, a clock from the HFXO stated as a multiple of the board's crystal | which pin a peripheral's signal reaches (its ROUTELOC or ROUTE location is the data sheet's), the blocks Renode does not model (run by nothing; their stores are reported), a pin held from outside on Series 0, PWM on pins, wire-level buses |
| GigaDevice GD32F10x, F30x, F403, E50x, F1x0, F3x0, F4xx | panel, MCP, CLI | everything above, from each twin's own register file (GigaDevice's firmware library headers and user manuals); the clock tree from the RCU, a clock from the HXTAL stated as a multiple of the board's crystal | which pin a peripheral's signal reaches (the database holds no pin routes for these parts), the USB blocks' registers, the EXMC's on the F405 and F425 and the comparators' on the E505, E507 and E508 (named and not judged), a PWM faster than the pins are sampled (reported from its registers), wire-level buses |
| FE310, Alif Ensemble and Balletto | panel, MCP, CLI | peripherals, pins, serial, the register check | the clock tree, I2C and SPI transactions |
| AVR | panel | loads and stores below SRAM, pins to the cycle, serial, TWI and SPI on classic parts, the register check | the clock tree, timers beyond the register table |
| RP2040 | panel | pins to the cycle, serial, boot, clocks, HardFault | peripherals, buses, timers, the register check: its twin keeps no register trace |
| ESP32-C3, C6, H2, P4 | panel | pins as read between batches, serial, why it halted | the same |
| ESP32, ESP32-S2, ESP32-S3 | panel | pins as read between batches, serial, on the S2 and S3 each general-purpose SPI controller's transfer count and last transfer | the same, I2C, the radio (no simulated far end here), and stimuli past a level held from reset |
| PIC32MX, PIC32MZ | panel | pins as the machine records each driven level's change, in board time; serial on the board's console UART; the blocks the firmware used that the machine does not model | peripherals, the clock tree, buses, timers, the register check: its twin keeps no register trace; stimuli past a level held from reset |
| 8051 | panel, MCP, CLI | pins to the machine cycle, UART bits off the pin, SFRs timers and interrupts were left with | peripherals by name and count, I2C and SPI |
Not explained yet: ST's STM32F3 on its second RCC, the F301, F302x6/8, F303x6/8, F318, F328 and F334 (stm32f334, stm32f302, stm32f303x8, stm32f301) and Nordic's nRF51822 (nrf51822). They run in the panel, but nothing here reads their blocks, pins, clocks or serial ports yet, so a request for one is refused by name rather than read as an STM32's.
The Silicon Labs twins
EFR32 Series 1 (efr32mg12, efr32mg13) and EFM32 Series 0 (efm32gg, efm32g, efm32wg), the twins ported from Renode (the Thunderboard Sense 2, the STK3700 and STK3800), are read by these rules, each from the part's reference manual (the EFR32xG12 and xG13, EFM32GG, EFM32G and EFM32WG reference manuals) and checked against Silicon Labs' own device headers and emlib:
- GPIO (EFR32xG12 RM 32.5, EFM32GG RM 32.5): each port's
MODELandMODEHgive a pin's mode, an input's pull taken fromDOUT;DOUTdrives a port's pins,DOUTTGLflips them, and on Series 0DOUTSETandDOUTCLRset and clear them. Series 1 has no set and clear registers: emlib stores to the peripheral bit-set and bit-clear windows at0x46000000and0x44000000(RM chapter 4), which reachDOUTas a set or a clear of the pins in the value, and the register check reads them so too. A port's pins are 0x30 apart on Series 1 and 0x24 on Series 0; the external interrupts'IENsays the firmware waits on a pin. - Clocks (EFR32xG12 RM 12, EFM32G, GG and WG RM 11.3.1): HFCLK is the oscillator
HFCLKSELchooses (Series 1, a register) or the lastCMD.HFCLKSELcommand (Series 0), divided byHFPRESC.PRESC + 1(Series 1) or the Giant and Wonder Geckos'CTRL.HFCLKDIV + 1; HFCORECLK and HFPERCLK divide it again (HFCOREPRESC,HFPERPRESC, or a shift byHFCORECLKDIV,HFPERCLKDIV). The HFRCO is 19 MHz out of reset on Series 1 and its 14 MHz band on Series 0; the LF branches (LFACLKSEL,LFBCLKSEL,LFECLKSEL, orLFCLKSEL) and their prescalers clock the LEUART, RTCC, RTC and LETIMER. The HFXO is the board's crystal: give it with--hse-hz(the STK3800's is 48 MHz, the Thunderboard's 38.4 MHz). Renode's Series 0 CMU answers nothing but its status, so only what the firmware stored is read. - Serial: a USART or UART is a serial port or, with
CTRL.SYNC, an SPI;TXDATA,TXDATAXand theTXDOUBLEpair send, theRXregisters receive, atHFPERCLK / (OVS x (1 + CLKDIV / 256))(EFR32xG12 RM 18, EFM32GG RM 17); a LEUART sends fromTXDATAat its LF clock/ (1 + CLKDIV / 256)(EFR32xG12 RM 19). - I2C (EFR32xG12 RM 17, EFM32GG RM 16): the first byte after
CMD.STARTis the address, loaded before the START or after it; then data untilSTOP;IF.NACKis no answer. SCL isHFPERCLK / ((Nlow + Nhigh) x (DIV + 1) + 8)(Series 1;+ 4on Series 0). - Timers (EFR32xG12 RM 20.5, EFM32GG RM 20.5, EFR32xG12 RM 24.5): a TIMER or WTIMER counts HFPERCLK through
CTRL.PRESCtoTOP(twice that up and down), a channel in PWM is high forCCVof it; the RTCC, the Series 0 RTC and LETIMER0 count their LF clocks to their compare values.
EFR32 Series 2 (efr32mg21, efr32mg22, efr32mg24, efr32mg26, efr32mg27, efr32mg29) is a line per memory map, read from each line's device header (Silicon Labs, Zlib) and emlib:
- Addresses (EFR32xG24 RM 4.2.4.3): each block answers in 16 KB, at its secure address and 0x10000000 above it, and its registers again at +0x1000 (a store sets the value's bits), +0x2000 (clears them) and +0x3000 (toggles them). All of these reach the one register, which is how a report states them, and a store through a window moves the pins or clocks in its mask.
- GPIO (EFR32xG24 RM 24.6): a port's
MODEL,MODEH,DOUTandDINas on Series 1, 0x30 apart, starting at +0 on the MG21 and xG22 and +0x30 on the others;IENat 0x424 says the firmware waits on a pin. Series 2 has noDOUTTGL: firmware toggles throughDOUT's window. - Clocks (EFR32xG24 RM 8.3 and 9.4): SYSCLK is what
SYSCLKCTRL.CLKSELchooses (the 20 MHz FSRCO out of reset), HCLK and the core divide it byHCLKPRESC + 1, PCLK divides that byPCLKPRESC + 1, and LSPCLK is PCLK / 2. HFRCODPLL is 19 MHz out of reset, and withDPLL0.ENits reference times(N + 1) / (M + 1). TIMERs count EM01GRPACLK; USARTs and I2C1 and up PCLK; I2C0 LSPCLK; EUSART0 or the xG22's EUART0 what itsCLKCTRLchooses, and the MG24's and MG26's other EUSARTs EM01GRPCCLK, as emlib'sCMU_ClockFreqGethas it. The HFXO is the board's crystal: give it with--hse-hz(39 MHz on an MG24 or MG26 board, 38.4 MHz on the others). - Serial: a USART is Series 1's with its registers 8 further on, on PCLK. An EUSART or EUART sends from
TXDATAand takes fromRXDATAwhere its line's header puts them, at its clock/ (OVS x (1 + CLKDIV / 256));CFG0.SYNCmakes an EUSART an SPI, clocked at its clock/ (CFG2.SDIV + 1). - I2C: Series 1's rule with its registers 8 further on, its SCL
/ ((Nlow + Nhigh) x (DIV + 1) + 8). - Timers (EFR32xG24 RM 19.5, 15.5; EFR32xG22 RM 15.5): a TIMER counts EM01GRPACLK through
CFG.PRESC + 1toTOP, twice that up and down, a channel in PWM high forCCn_OCof it; an RTCC or SYSRTC counts its LF clock over 32 bits, and a Zephyr kernel's tick is a compare on it.
uart1:, uart2: and so on name a part's serial blocks in order, USARTs, then UARTs, then LEUARTs (on the EFR32MG12, uart1: is USART0 and uart5: LEUART0; on the EFM32GG, uart4: is UART0; on Series 2 its USARTs, then its EUSARTs or EUART: on the MG24, uart1: is USART0 and uart2: EUSART0); i2c1@ puts a device on I2C0 and spi1@flash a flash chip on USART0 (Series 1 only), as on the RP2350.
The GigaDevice twins
The GD32F10x (gd32f10x, gd32f10x_hd, gd32f10x_xd, gd32f10x_cl), GD32F30x (gd32f30x, gd32f30x_xd, gd32f30x_cl), GD32F403 (gd32f403), GD32E50x (gd32e503, gd32e505, gd32e507, gd32e508), GD32F1x0 (gd32f130, gd32f150, gd32f170, gd32f190), GD32F3x0 (gd32f310, gd32f330, gd32f350) and GD32F4xx (gd32f405, gd32f407, gd32f425, gd32f427, gd32f450, gd32f470) twins are read by these rules, each from its line's user manual. Their register files are written from GigaDevice's firmware library headers (BSD-3-Clause: blocks, offsets, fields, clock gates) and the user manuals (access and reset values, as facts), not from its Keil packs' SVDs, which state no licence; a register no manual entry matches says so and is taken as read-write.
- Blocks count from 0 in the manuals' names: USART0, I2C0, SPI0, TIMER0. USART0, SPI0 and the fast timers (TIMER0, 7 to 10; on the F1x0 and F3x0 TIMER0, 14 to 16) are on APB2, as are the F4xx's and E50x's USART5 and the F4xx's SPI3 to 5; everything else is on APB1.
- GPIO: the F10x, F30x, F403 and E50x read as an STM32F1 (
CTL0,CTL1,OCTL,BOP,BC), the F1x0, F3x0 and F4xx as an STM32F4, withBCat 0x28 and a toggle register,TG, at 0x2C (F4xx UM 7.4). - Clocks: CK_SYS is
RCU_CFG0.SCS's choice of the IRC8M (the F4xx's IRC16M), the HXTAL or the PLL;PLLMF's extra bits are at 27 on the F10x, 29 on its connectivity line, 27 and 30 on the F303 and E503, 30:29 on the F305, F307, F403, E505, E507 and E508, 27 andRCU_CFG1bit 31 on the F3x0 (F10x UM 5.3.2, 5.6.2; F30x UM 5.3.2, 5.6.2; E50x UM 5.3.2, 5.6.2; F3x0 UM 4.3.2). The connectivity line's PLL goes throughPREDV0and, withPREDV0SEL, PLL1 (PREDV1,PLL1MF); the F30x and F3x0 may start from the IRC48M; the F4xx has the STM32F4'sRCU_PLLandRCU_CFG1.TIMERSEL's timer rule (F4xx UM 5.3.25). The HXTAL is the board's crystal: give it with--hse-hz(GigaDevice's library assumes 8 MHz on the F10x, F303, E503, F1x0 and F3x0, 25 MHz on the connectivity lines, F403 and F4xx). On the F1x0 and F3x0 USART0 has its own clock,RCU_CFG2.USART0SEL, and on the E50x USART5,RCU_CFG2.USART5SEL. - Serial: a port sends at its clock over
USART_BAUD, withOVSMODon the F1x0, F3x0, F4xx and E50x (F10x UM 16.3.2, F3x0 UM 17.3.2, F4xx UM 19.3.2);DATAcarries bytes, or on the F1x0's, F3x0's and the E50x's USART5,TDATAandRDATA. - I2C and SPI: the STM32's first layout on every line in the manuals' names:
CTL0.STARTthen the first store toDATAis the address,STAT0.ADDSENDorAERRsays if it answered, SCL is its clock over2 x CKCFG.CLKC(F10x UM 17.4); an SPI's clock is its bus's overCTL0.PSC(F10x UM 18.11). The E50x's I2C2 is the later layout (E50x UM 21.2):CTL1holds the address,TDATAandRDATAthe bytes. - Timers:
PSC,CARandCH0CVtoCH3CV, at the bus clock doubled when its prescaler divides, byTIMERSELon the F4xx.
uart1:, uart2: and so on name USART0, USART1, USART2, UART3, UART4, then the F4xx's USART5, UART6 and UART7 and the E50x's USART5; i2c1@ puts a device on I2C0 and spi1@flash a flash chip on SPI0.
Times and frequencies count the twin's board time, one instruction per cycle of its clock. A software delay loop or a timer's rate on a board depends on the real clock and core timing, and a report says so when the clock the firmware configured differs from the one the twin counts at.