From 89c4eeee8c2a723a73112c4319ee2c820bb9f427 Mon Sep 17 00:00:00 2001 From: Ayushman Date: Mon, 8 Jun 2026 16:24:21 +0530 Subject: [PATCH 1/5] am243x/am64x/am261x: add uart config and operation Signed-off-by: Ayushman a-ayushman@ti.com --- .../pru_io_blocks/uart_config.syscfg.js | 708 +++++++++++++ .../pru_io_blocks/uart_rx_op.syscfg.js | 629 +++++++++++ .../pru_io_blocks/uart_tx_op.syscfg.js | 989 ++++++++++++++++++ .../.meta/soc/pru_blocks_am243x.syscfg.js | 6 +- .../.meta/soc/pru_blocks_am261x.syscfg.js | 6 +- .../.meta/soc/pru_blocks_am64x.syscfg.js | 6 +- 6 files changed, 2338 insertions(+), 6 deletions(-) create mode 100644 .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_config.syscfg.js create mode 100644 .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx_op.syscfg.js create mode 100644 .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx_op.syscfg.js diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_config.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_config.syscfg.js new file mode 100644 index 0000000..e435bab --- /dev/null +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_config.syscfg.js @@ -0,0 +1,708 @@ +/** + * Helper function to extract PRU number from system context + * @returns {number} PRU number (0 or 1), defaults to 0 if cannot determine + */ +/** + * Read txDataBits from the uart_tx_op instance if one exists. + * Falls back to 8 if no uart_tx_op is present. + */ +function getTxDataBits() { + const txOpModule = system.modules["/pru_blocks/pru_io_blocks/uart_tx_op"]; + if (txOpModule && txOpModule.$instances && txOpModule.$instances.length > 0) { + return txOpModule.$instances[0].dataBits; + } + return 8; +} + +/** + * Read rxFrameSize from the uart_rx_op instance if one exists. + * Falls back to 10 if no uart_rx_op is present. + */ +function getRxFrameSize() { + const rxOpModule = system.modules["/pru_blocks/pru_io_blocks/uart_rx_op"]; + if (rxOpModule && rxOpModule.$instances && rxOpModule.$instances.length > 0) { + return rxOpModule.$instances[0].rxFrameSize; + } + return 10; +} + +function getPruNumberFromContext() { + const common = system.getScript("/common"); + const coreName = common.getSelfSysCfgCoreName(); + if (coreName && coreName.includes("pru")) { + const match = coreName.match(/pru(\d+)$/); + if (match && match[1]) { + return parseInt(match[1]); + } + } + return 0; +} + +function validate(inst, report) { + // At least one of TX or RX must be enabled + if (!inst.enableTX && !inst.enableRX) { + report.logError("At least one of TX Config or RX Config must be enabled.", inst, "enableTX"); + report.logError("At least one of TX Config or RX Config must be enabled.", inst, "enableRX"); + } + + if (inst.enableTX) { + const uartClkMHz = inst.txClockSource === 0 ? 192 : 200; + if (uartClkMHz % inst.txBaudRate !== 0) { + report.logError( + `TX: Clock (${uartClkMHz} MHz) is not divisible by baudRate = ${inst.txBaudRate} MHz.`, + inst, "txBaudRate"); + } else { + const div = (uartClkMHz / inst.txBaudRate) - 1; + if (div < 0 || div > 65535) { + report.logError( + `TX: Calculated clock divider (${div}) is out of range (0-65535).`, + inst, "txBaudRate"); + } + } + } + + if (inst.enableRX) { + let oversampleMul; + if (inst.rxOversampleSize === 0) oversampleMul = 1; + else if (inst.rxOversampleSize === 1) oversampleMul = 2; + else if (inst.rxOversampleSize === 3) oversampleMul = 4; + else oversampleMul = 8; + + const uartClkMHz = inst.rxClockSource === 0 ? 192 : 200; + const baudXOs = inst.rxBaudRate * oversampleMul; + if (uartClkMHz % baudXOs !== 0) { + report.logError( + `RX: Clock (${uartClkMHz} MHz) is not divisible by (baudRate * oversample) = ${baudXOs}.`, + inst, "rxBaudRate"); + } else { + const div = (uartClkMHz / baudXOs) - 1; + if (div < 0 || div > 65535) { + report.logError( + `RX: Calculated clock divider (${div}) is out of range (0-65535).`, + inst, "rxBaudRate"); + } + } + } +} + +/** + * Returns guarded ICSS_CFG register symbol definitions (TX + RX registers). + */ +function getIcssCfgDefinitions() { + return `\ +; ========== ICSS CFG register definitions (auto-generated, guarded) ========== + .if !$isdefed("ICSS_CFG") + .asg c4, ICSS_CFG + .endif + .if !$isdefed("ICSS_CFG_GPCFG0") +ICSS_CFG_GPCFG0 .set 0x0008 + .endif + .if !$isdefed("ICSS_CFG_GPCFG1") +ICSS_CFG_GPCFG1 .set 0x000C + .endif + .if !$isdefed("ICSS_CFG_PRU0_ENDAT_TXCFG") +ICSS_CFG_PRU0_ENDAT_TXCFG .set 0x00E4 + .endif + .if !$isdefed("ICSS_CFG_PRU1_ENDAT_TXCFG") +ICSS_CFG_PRU1_ENDAT_TXCFG .set 0x0104 + .endif + .if !$isdefed("ICSS_CFG_PRU0_ENDAT_RXCFG") +ICSS_CFG_PRU0_ENDAT_RXCFG .set 0x00E0 + .endif + .if !$isdefed("ICSS_CFG_PRU1_ENDAT_RXCFG") +ICSS_CFG_PRU1_ENDAT_RXCFG .set 0x0100 + .endif + .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH0_CFG0") +ICSS_CFG_PRU0_ENDAT_CH0_CFG0 .set 0x00E8 + .endif + .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH1_CFG0") +ICSS_CFG_PRU0_ENDAT_CH1_CFG0 .set 0x00F0 + .endif + .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH2_CFG0") +ICSS_CFG_PRU0_ENDAT_CH2_CFG0 .set 0x00F8 + .endif + .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH0_CFG0") +ICSS_CFG_PRU1_ENDAT_CH0_CFG0 .set 0x0108 + .endif + .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH1_CFG0") +ICSS_CFG_PRU1_ENDAT_CH1_CFG0 .set 0x0110 + .endif + .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH2_CFG0") +ICSS_CFG_PRU1_ENDAT_CH2_CFG0 .set 0x0118 + .endif +; ============================================================================ +`; +} + +/** + * Returns the macro body for the combined UART Config block. + * + * Sequence: + * 1. Global Reinit FIRST (TRM-mandated: reinit before de-asserting rx_en) + * 2. Poll busy bit (channel 0 busy bit covers the peripheral reinit) + * 3. De-assert rx_en AFTER reinit (handles stuck rx_en from previous run) + * 4. GPCFG write (once — same PRU for TX and RX) + * 5. [if TX] TXCFG write, TX CH_CFG0 write, r30.w2 channel select + clk_mode=1 + * 6. [if RX] RXCFG write, RX CH_CFG0 write + */ +function getMacro(pruInstructionMacro, opCode) { + const prefix = (pruInstructionMacro === "") ? getIcssCfgDefinitions() : ""; + + const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); + const macroName = parts[0] || opCode; + + // Decode what is enabled from macro name + const hasTX = macroName.includes("_tx_"); + const hasRX = macroName.includes("_rx_"); + + const isPRU0 = macroName.includes("_pru0"); + const pruNum = isPRU0 ? "0" : "1"; + const gpcfgReg = isPRU0 ? "ICSS_CFG_GPCFG0" : "ICSS_CFG_GPCFG1"; + + // TX register aliases + const txcfgReg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_TXCFG" : "ICSS_CFG_PRU1_ENDAT_TXCFG"; + const txCh0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH0_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH0_CFG0"; + const txCh1Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH1_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH1_CFG0"; + const txCh2Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH2_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH2_CFG0"; + + // RX register aliases + const rxcfgReg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_RXCFG" : "ICSS_CFG_PRU1_ENDAT_RXCFG"; + const rxCh0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH0_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH0_CFG0"; + const rxCh1Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH1_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH1_CFG0"; + const rxCh2Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH2_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH2_CFG0"; + + // Parse macro name for TX section params + // TX section in name: _tx_ch{N}_lsb|msb_start{N}[_stop{N}]_ss|cont + let txChannel = "0", txIsLSB = true, txIsSS = true, txHasExplicitStop = false, txStopBit = "0"; + if (hasTX) { + const txChMatch = macroName.match(/_tx_ch(\d)_/); + if (txChMatch) txChannel = txChMatch[1]; + txIsLSB = macroName.includes("_tx_ch" + txChannel + "_lsb_"); + txIsSS = macroName.includes("_ss"); + const stopMatch = macroName.match(/_tx_[^_]+_[^_]+_start\d_stop(\d)/); + if (stopMatch) { txHasExplicitStop = true; txStopBit = stopMatch[1]; } + } + + // Parse macro name for RX section params + // RX section in name: _rx_ch{N}_lsb|msb_1x|2x|4x|8x + let rxChannel = "0", rxIsLSB = true; + if (hasRX) { + const rxChMatch = macroName.match(/_rx_ch(\d)_/); + if (rxChMatch) rxChannel = rxChMatch[1]; + rxIsLSB = macroName.includes("_rx_ch" + rxChannel + "_lsb_"); + } + + // Parameter positions depend on what's enabled + // For TX+RX: macro params = txChannel, txClockDiv, txClkSel, txDataBits, rxChannel, rxClockDiv, rxClkSel, rxOversample, rxStartBitPol, rxFrameSize + // For TX only: txChannel, txClockDiv, txClkSel, txDataBits + // For RX only: rxChannel, rxClockDiv, rxClkSel, rxOversample, rxStartBitPol, rxFrameSize + let paramIdx = 1; + let txCh, txClockDiv, txClkSel, txDataBits; + let rxCh, rxClockDiv, rxClkSel, rxOversample, rxStartBitPol, rxFrameSize; + + if (hasTX) { + txCh = parts[paramIdx++] || "txChannel"; + txClockDiv = parts[paramIdx++] || "txClockDiv"; + txClkSel = parts[paramIdx++] || "txClkSel"; + txDataBits = parts[paramIdx++] || "txDataBits"; + } + if (hasRX) { + rxCh = parts[paramIdx++] || "rxChannel"; + rxClockDiv = parts[paramIdx++] || "rxClockDiv"; + rxClkSel = parts[paramIdx++] || "rxClkSel"; + rxOversample = parts[paramIdx++] || "rxOversample"; + rxStartBitPol = parts[paramIdx++] || "rxStartBitPol"; + rxFrameSize = parts[paramIdx++] || "rxFrameSize"; + } + + let macroBody = ""; + + if (pruInstructionMacro === "") { + // Build macro signature + let sigParams = []; + if (hasTX) sigParams.push("txChannel", "txClockDiv", "txClkSel", "txDataBits"); + if (hasRX) sigParams.push("rxChannel", "rxClockDiv", "rxClkSel", "rxOversample", "rxStartBitPol", "rxFrameSize"); + macroBody = macroName + "\t.macro " + sigParams.join(", ") + "\n"; + macroBody += `\t; UART Combined Config — PRU${pruNum}${hasTX ? ", TX enabled" : ""}${hasRX ? ", RX enabled" : ""}\n`; + } + + macroBody += ` +\t; ========== Step 1: Global Reinit (TRM-mandated order: reinit BEFORE de-asserting rx_en) ========== +\t; If rx_en was left HIGH by a previous stuck RX, reinit resets the RX state machine first. +\tset r31, r31, 19 ; 1. Global Reinit — clears FIFO and state machines + +\t; ========== Step 2: Poll Reinit Busy Bit (channel 0) ========== +wait_reinit?: +\tqbbs wait_reinit?, r31, 5 +\tldi r30.b3, 0x00 ; 3. De-assert rx_en AFTER reinit confirmed complete + +\t; ========== Step 3: GPCFG — Enable Peripheral Mode for PRU${pruNum} ========== +\tldi32 TEMP_REG1, 0x04000000 +\tldi TEMP_REG2.w0, ${gpcfgReg} +\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 +`; + + if (hasTX) { + const isSingleShot = txIsSS; + // Shift by 3 because we write byte 1 (+1 offset) only. + // TX_FRAME_SIZE lives in CFG0[15:11]. Within byte 1 (CFG0[15:8]), that is bits[7:3]. + const frameSizeExpr = isSingleShot ? `(${txDataBits} + 2) << 3` : `0`; + const frameSizeComment = isSingleShot + ? `; byte1[7:3] = TX_FRAME_SIZE = txDataBits + 2 (single-shot)` + : `; byte1[7:3] = 0 (continuous mode)`; + + macroBody += ` +\t; ========== Step 4: TX Clock Configuration (TXCFG) ========== +\t; [31:16] = clockDiv, [4] = TX_CLK_SEL (0=192MHz, 1=200MHz) +\tldi TEMP_REG1.w0, (${txClkSel} << 4) +\tldi TEMP_REG1.w2, ${txClockDiv} +\tldi TEMP_REG2.w0, ${txcfgReg} +\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 + +\t; ========== Step 5: TX Channel CFG0 — TX frame size (byte 1) + TX_FIFO_SWAP_BITS (byte 3) ========== +\t; Byte 1 (+1 offset): read-modify-write — preserve TX_WDLY[10:8], set TX_FRAME_SIZE[15:11] +\t; ${frameSizeComment} +\t.if ${txCh} == 0 +\tldi TEMP_REG2.w0, ${txCh0Reg} + 1 +\t.elseif ${txCh} == 1 +\tldi TEMP_REG2.w0, ${txCh1Reg} + 1 +\t.else +\tldi TEMP_REG2.w0, ${txCh2Reg} + 1 +\t.endif +\tlbco &TEMP_REG1.b0, ICSS_CFG, TEMP_REG2.w0, 1 ; read existing byte 1 +\tand TEMP_REG1.b0, TEMP_REG1.b0, 0x07 ; clear TX_FRAME_SIZE[15:11], keep WDLY[10:8] +\tor TEMP_REG1.b0, TEMP_REG1.b0, ${frameSizeExpr} ; set TX_FRAME_SIZE +\tsbco &TEMP_REG1.b0, ICSS_CFG, TEMP_REG2.w0, 1 ; write byte 1 back only +\t; Byte 3 (+3 offset): TX_FIFO_SWAP_BITS = bit 31 of CFG0 = bit 7 of byte 3 +\t; ${txIsLSB ? "LSB first: set TX_FIFO_SWAP_BITS=1" : "MSB first: clear TX_FIFO_SWAP_BITS=0"} +\t.if ${txCh} == 0 +\tldi TEMP_REG2.w0, ${txCh0Reg} + 3 +\t.elseif ${txCh} == 1 +\tldi TEMP_REG2.w0, ${txCh1Reg} + 3 +\t.else +\tldi TEMP_REG2.w0, ${txCh2Reg} + 3 +\t.endif +\tlbco &TEMP_REG1.b0, ICSS_CFG, TEMP_REG2.w0, 1 ; read existing byte 3 +${txIsLSB + ? `\tset TEMP_REG1.b0, TEMP_REG1.b0, 7 ; set bit 7 = TX_FIFO_SWAP_BITS (LSB first)` + : `\tclr TEMP_REG1.b0, TEMP_REG1.b0, 7 ; clear bit 7 = TX_FIFO_SWAP_BITS (MSB first)`} +\tsbco &TEMP_REG1.b0, ICSS_CFG, TEMP_REG2.w0, 1 ; write byte 3 back only + +\t; ========== Step 6: TX Channel Select + clk_mode=1 ========== +\t; clk_mode=1: free-running, stops HIGH after last RX frame +\t; This keeps the clock running after TX so the encoder can clock its response back. +\t.if ${txCh} == 0 +\tldi r30.w2, 0x0008 ; clk_mode=1 (bits[20:19]=0b01), ch=0 +\t.elseif ${txCh} == 1 +\tldi r30.w2, 0x0009 ; clk_mode=1, ch=1 +\t.else +\tldi r30.w2, 0x000A ; clk_mode=1, ch=2 +\t.endif +`; + } + + if (hasRX) { + macroBody += ` +\t; ========== Step ${hasTX ? "7" : "4"}: RX Clock Configuration (RXCFG) ========== +\t; [31:16] = clockDiv, [4] = RX_CLK_SEL, [3] = startBitPol, [2:0] = oversample encoding +\tldi TEMP_REG1.w0, (${rxClkSel} << 4) | ${rxOversample} +`; + // startBitPol is a runtime param — use assembler conditional + macroBody += `\t.if ${rxStartBitPol} != 0 +\tset TEMP_REG1.w0, TEMP_REG1.w0, 3 ; Set bit 3 (startBitPol=1) +\t.endif +\tldi TEMP_REG1.w2, ${rxClockDiv} +\tldi TEMP_REG2.w0, ${rxcfgReg} +\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 + +\t; ========== Step ${hasTX ? "8" : "5"}: RX Channel CFG0 — RX frame size + bitSwap (bytes 2-3) ========== +\t; Writes bytes 2-3 (+2 offset, 2 bytes) only — does NOT touch byte 1 (TX frame size). +\t; [27:16] = RX_FRAME_SIZE, [31] = bitSwap (LSB first) +\t.if ${rxCh} == 0 +\tldi TEMP_REG2.w0, ${rxCh0Reg} + 2 +\t.elseif ${rxCh} == 1 +\tldi TEMP_REG2.w0, ${rxCh1Reg} + 2 +\t.else +\tldi TEMP_REG2.w0, ${rxCh2Reg} + 2 +\t.endif +\tldi TEMP_REG1.w0, ${rxFrameSize} ; RX_FRAME_SIZE in bits[27:16] of this 2-byte write +`; + if (rxIsLSB) { + macroBody += `\tset TEMP_REG1.w0, TEMP_REG1.w0, 15 ; Set bit 31 of CFG0 (bitSwap=1, LSB first) — bit 15 of this 2-byte write\n`; + } + macroBody += `\tsbco &TEMP_REG1.w0, ICSS_CFG, TEMP_REG2.w0, 2 ; write bytes 2-3 only +`; + } + + if (pruInstructionMacro === "") { + macroBody += "\n .endm"; + } + + return prefix + macroBody; +} + +function getLongDescription() { + return ` +## UART Config (Combined TX + RX Hardware Configuration) + +### Purpose +Configures the PRU-ICSS ENDAT peripheral for UART TX, RX, or both. Run this block +**once at init**. A single Global Reinit covers both TX and RX setup. + +Use the **UART TX Op** block for per-transmission operations and the +**UART RX Op** block for per-reception operations. + +### Enable Checkboxes +- **Enable TX Config**: Show and apply TX configuration parameters +- **Enable RX Config**: Show and apply RX configuration parameters +- At least one must be enabled. + +### Generated Sequence +1. Global Reinit (r31 bit 19) FIRST — flushes FIFO and state machines (TRM-mandated order) +2. Poll busy bit — deterministic wait for reinit completion +3. De-assert rx_en AFTER reinit — handles stuck rx_en from a previous run +4. GPCFG write — enable peripheral interface mode (once, shared by TX and RX) +5. *(if TX enabled)* TXCFG write, TX CH_CFG0 write, r30.w2 channel select + clk_mode=1 +6. *(if RX enabled)* RXCFG write, RX CH_CFG0 write + +### Notes +- TX and RX can use **different channels** (e.g. TX on CH0, RX on CH2) +- Only one instance of this block is allowed per design (\`maxInstances: 1\`) +- GPCFG is written once regardless of whether TX-only, RX-only, or both are enabled +`; +} + +exports = { + displayName: "PRU UART Config", + defaultInstanceName: "PRU_UART_CONFIG_", + maxInstances: 1, + longDescription: getLongDescription(), + uiView: "graph", + templates: { + "/pru_blocks/common/pru_syscfg.asm.xdt": null + }, + config: [ + { + name: "$shape", + hidden: true, + default: "square", + }, + { + name: "$topLabel", + hidden: true, + default: "", + }, + { + name: "pruSelect", + displayName: "PRU Selection", + description: "Auto-detected from system context (read-only)", + default: getPruNumberFromContext(), + readOnly: true, + options: [ + { name: 0, displayName: "PRU0" }, + { name: 1, displayName: "PRU1" } + ] + }, + + // ===== TX CONFIG ===== + { + name: "enableTX", + displayName: "Enable TX Config", + description: "Enable TX peripheral configuration. Required if using UART TX Op block.", + default: true, + onChange: function(inst, ui) { + const show = inst.enableTX; + ui.txChannel.hidden = !show; + ui.txClockSource.hidden = !show; + ui.txBaudRate.hidden = !show; + ui.txClockDivider.hidden = !show; + ui.txStartBitPolarity.hidden = !show; + ui.txStopBitPolarity.hidden = !show; + ui.txBitSwap.hidden = !show; + ui.txMode.hidden = !show; + } + }, + { + name: "txChannel", + displayName: "TX Channel", + description: "ENDAT channel for UART TX (0, 1, or 2)", + default: 0, + hidden: false, + options: [ + { name: 0, displayName: "Channel 0" }, + { name: 1, displayName: "Channel 1" }, + { name: 2, displayName: "Channel 2" } + ] + }, + { + name: "txClockSource", + displayName: "TX Clock Source", + description: "192 MHz (ICSSGn_UART_CLK) or 200 MHz (ICSSGn_CORE_CLK)", + default: 0, + hidden: false, + options: [ + { name: 0, displayName: "192 MHz (UART_CLK)" }, + { name: 1, displayName: "200 MHz (CORE_CLK)" } + ] + }, + { + name: "txBaudRate", + displayName: "TX Baud Rate (MHz)", + description: "Desired TX baud rate in MHz. Clock must be divisible by baudRate.", + default: 12, + hidden: false, + displayFormat: "dec" + }, + { + name: "txClockDivider", + displayName: "TX Clock Divider (Calculated)", + description: "Calculated TX clock divider = (clockSource / baudRate) - 1", + default: 15, + readOnly: true, + hidden: false, + getValue: (inst) => { + const clkMHz = inst.txClockSource === 0 ? 192 : 200; + if (inst.txBaudRate === 0 || clkMHz % inst.txBaudRate !== 0) return 0; + return (clkMHz / inst.txBaudRate) - 1; + } + }, + { + name: "txStartBitPolarity", + displayName: "TX Start Bit Polarity", + description: "Polarity of TX start bit", + default: 1, + hidden: false, + options: [ + { name: 0, displayName: "0 (Low/Space)" }, + { name: 1, displayName: "1 (High/Mark)" } + ] + }, + { + name: "txStopBitPolarity", + displayName: "TX Stop Bit Polarity", + description: "Polarity of TX stop bit", + default: 0, + hidden: false, + options: [ + { name: 0, displayName: "0 (Low/Space)" }, + { name: 1, displayName: "1 (High/Mark)" } + ] + }, + { + name: "txBitSwap", + displayName: "TX Bit Swap (LSB First)", + description: "Enable LSB-first transmission (standard UART). Disable for MSB-first.", + default: true, + hidden: false, + }, + { + name: "txMode", + displayName: "TX Mode", + description: "Transmission mode based on TX data bits", + hidden: false, + readOnly: true, + getValue: (_inst) => getTxDataBits() <= 29 ? "Single-shot" : "Continuous", + default: "Single-shot" + }, + // ===== RX CONFIG ===== + { + name: "enableRX", + displayName: "Enable RX Config", + description: "Enable RX peripheral configuration. Required if using UART RX Op block.", + default: true, + onChange: function(inst, ui) { + const show = inst.enableRX; + ui.rxChannel.hidden = !show; + ui.rxClockSource.hidden = !show; + ui.rxBaudRate.hidden = !show; + ui.rxClockDivider.hidden = !show; + ui.rxOversampleSize.hidden = !show; + ui.rxStartBitPolarity.hidden = !show; + ui.rxBitSwap.hidden = !show; + } + }, + { + name: "rxChannel", + displayName: "RX Channel", + description: "ENDAT channel for UART RX (0, 1, or 2)", + default: 2, + hidden: false, + options: [ + { name: 0, displayName: "Channel 0" }, + { name: 1, displayName: "Channel 1" }, + { name: 2, displayName: "Channel 2" } + ] + }, + { + name: "rxClockSource", + displayName: "RX Clock Source", + description: "192 MHz (ICSSGn_UART_CLK) or 200 MHz (ICSSGn_CORE_CLK)", + default: 0, + hidden: false, + options: [ + { name: 0, displayName: "192 MHz (UART_CLK)" }, + { name: 1, displayName: "200 MHz (CORE_CLK)" } + ] + }, + { + name: "rxBaudRate", + displayName: "RX Baud Rate (MHz)", + description: "Desired RX baud rate in MHz. Clock must be divisible by (baudRate * oversample).", + default: 12, + hidden: false, + displayFormat: "dec" + }, + { + name: "rxClockDivider", + displayName: "RX Clock Divider (Calculated)", + description: "Calculated RX clock divider = (clockSource / (baudRate * oversample)) - 1", + default: 1, + readOnly: true, + hidden: false, + getValue: (inst) => { + let osMul; + if (inst.rxOversampleSize === 0) osMul = 1; + else if (inst.rxOversampleSize === 1) osMul = 2; + else if (inst.rxOversampleSize === 3) osMul = 4; + else osMul = 8; + const clkMHz = inst.rxClockSource === 0 ? 192 : 200; + const bxo = inst.rxBaudRate * osMul; + if (bxo === 0 || clkMHz % bxo !== 0) return 0; + return (clkMHz / bxo) - 1; + } + }, + { + name: "rxOversampleSize", + displayName: "RX Oversample Size", + description: "Samples per bit. Higher = better noise immunity.", + default: 7, + hidden: false, + options: [ + { name: 0, displayName: "1x" }, + { name: 1, displayName: "2x" }, + { name: 3, displayName: "4x" }, + { name: 7, displayName: "8x" } + ] + }, + { + name: "rxStartBitPolarity", + displayName: "RX Start Bit Polarity", + description: "Edge that indicates start of RX frame (0=Falling, 1=Rising)", + default: 1, + hidden: false, + options: [ + { name: 0, displayName: "Falling Edge (0)" }, + { name: 1, displayName: "Rising Edge (1)" } + ] + }, + { + name: "rxBitSwap", + displayName: "RX Bit Swap (LSB First)", + description: "Enable LSB-first reception (standard UART). Disable for MSB-first.", + default: true, + hidden: false, + }, + + // ===== HIDDEN CODE GEN FIELDS ===== + { + name: "constant1", + hidden: true, + default: "0, 15, 0, 8", + getValue: (inst) => { + let params = []; + if (inst.enableTX) { + const clkMHz = inst.txClockSource === 0 ? 192 : 200; + const txDiv = (inst.txBaudRate === 0 || clkMHz % inst.txBaudRate !== 0) + ? 0 : (clkMHz / inst.txBaudRate) - 1; + params.push(inst.txChannel, txDiv, inst.txClockSource, getTxDataBits()); + } + if (inst.enableRX) { + let osMul; + if (inst.rxOversampleSize === 0) osMul = 1; + else if (inst.rxOversampleSize === 1) osMul = 2; + else if (inst.rxOversampleSize === 3) osMul = 4; + else osMul = 8; + const clkMHz = inst.rxClockSource === 0 ? 192 : 200; + const bxo = inst.rxBaudRate * osMul; + const rxDiv = (bxo === 0 || clkMHz % bxo !== 0) + ? 0 : (clkMHz / bxo) - 1; + params.push(inst.rxChannel, rxDiv, inst.rxClockSource, + inst.rxOversampleSize, inst.rxStartBitPolarity, getRxFrameSize()); + } + return params.join(", "); + } + }, + { + name: "opCode", + displayName: "m_uart_config", + default: "m_uart_config", + hidden: true, + getValue: (inst) => { + const pruNum = inst.pruSelect; + let name = `m_uart_config_pru${pruNum}`; + + if (inst.enableTX) { + const bitOrder = inst.txBitSwap ? "lsb" : "msb"; + const startBit = inst.txStartBitPolarity; + const stopBit = inst.txStopBitPolarity; + const needsStop = (startBit === stopBit); + const stopSuffix = needsStop ? `_stop${stopBit}` : ""; + const modeSuffix = getTxDataBits() <= 29 ? "_ss" : "_cont"; + name += `_tx_ch${inst.txChannel}_${bitOrder}_start${startBit}${stopSuffix}${modeSuffix}`; + } + + if (inst.enableRX) { + const bitOrder = inst.rxBitSwap ? "lsb" : "msb"; + let osName; + if (inst.rxOversampleSize === 0) osName = "1x"; + else if (inst.rxOversampleSize === 1) osName = "2x"; + else if (inst.rxOversampleSize === 3) osName = "4x"; + else osName = "8x"; + name += `_rx_ch${inst.rxChannel}_${bitOrder}_${osName}`; + } + + return name; + } + }, + { + name: "outputReg", + default: "None", + hidden: true + }, + { + name: "numOfInputPorts", + default: 0, + hidden: true + }, + { + name: "numOfOutputPorts", + default: 0, + hidden: true + }, + { + name: "numOfConstants", + default: 1, + hidden: true + }, + { + name: "noOfCycles", + displayName: "Number Of Cycles", + getValue: (_inst) => 20, + default: 20 + }, + ], + ports: (_inst) => [ + { name: "prev", type: "PREV" }, + { name: "next", type: "NEXT" }, + ], + validate, + moduleStatic: { + modules: function(inst) { + return [{ + name: "pru_register_allocator_validator", + moduleName: "/pru_blocks/common/pru_blocks_static_module" + }] + }, + }, + getMacro +} diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx_op.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx_op.syscfg.js new file mode 100644 index 0000000..0bae2bb --- /dev/null +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx_op.syscfg.js @@ -0,0 +1,629 @@ +/** + * UART RX Op block. + * Assumes peripheral is already configured by uart_config block. + * Only does: assert rx_en → bit polling loop → data extraction → de-assert rx_en. + */ + +/** + * Get the uart_config instance bound to this op block via sharedModuleInstances. + * inst.uartConfig is auto-populated by SysConfig when the shared instance is created. + */ +function getConfigInst(inst) { + return (inst && inst.uartConfig) ? inst.uartConfig : null; +} + +function validate(inst, report) { + const cfg = inst.uartConfig; + if (!cfg) { + report.logError( + "A UART Config block is required before using UART RX Op. " + + "Add a UART Config block with RX Config enabled and connect it earlier in the control flow.", + inst + ); + return; + } + if (!cfg.enableRX) { + report.logError( + "The UART Config block does not have RX Config enabled. " + + "Enable RX Config in the UART Config block.", + inst + ); + } + + // Enforce all uart_rx_op instances use the same rxFrameSize value + const rxOpModule = system.modules["/pru_blocks/pru_io_blocks/uart_rx_op"]; + if (rxOpModule && rxOpModule.$instances && rxOpModule.$instances.length > 1) { + const firstFrameSize = rxOpModule.$instances[0].rxFrameSize; + const mismatch = rxOpModule.$instances.some(i => i.rxFrameSize !== firstFrameSize); + if (mismatch) { + report.logError( + `All UART RX Op blocks must have the same RX Frame Size value. ` + + `uart_config uses the first instance's value (${firstFrameSize} bits) for RX configuration — ` + + `mismatched instances will generate incorrect assembly.`, + inst, "rxFrameSize" + ); + } + } +} + +/** + * Returns the macro body for the UART RX Op block. + * Mirrors the original uart_rx.syscfg.js bit loop and extraction exactly. + * Only difference: no peripheral config writes — those are in uart_config block. + * + * Sequence: + * 1. Assert rx_en (resolved at code-gen time from channel) + * 2. Loop frameSize times: qbbc poll, read middle sample, set to clear, accumulate + * 3. Extract data (identical to original uart_rx — remove start/stop bits) + * 4. Disable rx_en + */ +function getMacro(pruInstructionMacro, opCode) { + let macroBody = ""; + + const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); + const macroName = parts[0] || opCode; + + const isExtended = macroName.includes("_ext"); + const isLSB = macroName.includes("_lsb_"); + + // Oversample middle bit — resolved at code-gen time from macro name + let middleBit; + if (macroName.includes("_1x")) middleBit = 0; + else if (macroName.includes("_2x")) middleBit = 0; + else if (macroName.includes("_4x")) middleBit = 2; + else middleBit = 4; // 8x default + + // Parameter positions — same as original uart_rx + let dataByteRegLo, dataByteRegHi, channel, clockDiv, rxClkSel, oversample, startBitPol, frameSize, bitSwap; + + if (isExtended) { + dataByteRegLo = parts[1] || "dataByteRegLo"; + dataByteRegHi = parts[2] || "dataByteRegHi"; + channel = parts[3] || "channel"; + clockDiv = parts[4] || "clockDiv"; + rxClkSel = parts[5] || "rxClkSel"; + oversample = parts[6] || "oversample"; + startBitPol = parts[7] || "startBitPol"; + frameSize = parts[8] || "frameSize"; + bitSwap = parts[9] || "bitSwap"; + } else { + dataByteRegLo = parts[1] || "dataByteRegLo"; + dataByteRegHi = "0"; + channel = parts[2] || "channel"; + clockDiv = parts[3] || "clockDiv"; + rxClkSel = parts[4] || "rxClkSel"; + oversample = parts[5] || "oversample"; + startBitPol = parts[6] || "startBitPol"; + frameSize = parts[7] || "frameSize"; + bitSwap = parts[8] || "bitSwap"; + } + + // Channel-dependent values are emitted as assembler .if conditionals + // so they resolve correctly at assemble time from the macro parameter, + // not at JS code-gen time when channel is still the placeholder string. + + const bitAccumReg = "TEMP_REG1"; + const bitCounterReg = isExtended ? "TEMP_REG1.b0" : "TEMP_REG2.b0"; + const tempSampleReg = isExtended ? "TEMP_REG1.b1" : "TEMP_REG2.b1"; + + if (pruInstructionMacro === "") { + if (isExtended) { + macroBody = macroName + "\t.macro " + dataByteRegLo + ", " + dataByteRegHi + ", " + + channel + ", " + clockDiv + ", " + rxClkSel + ", " + oversample + ", " + + startBitPol + ", " + frameSize + ", " + bitSwap + "\n"; + } else { + macroBody = macroName + "\t.macro " + dataByteRegLo + ", " + + channel + ", " + clockDiv + ", " + rxClkSel + ", " + oversample + ", " + + startBitPol + ", " + frameSize + ", " + bitSwap + "\n"; + } + macroBody += `\t; UART RX Op — no peripheral config writes +\t; uart_config block must be called before this block +`; + } + + // Assert rx_en — use assembler .if so it resolves from the macro parameter + macroBody += ` +\t; ========== Enable RX for selected channel ========== +\t.if ${channel} == 0 +\tldi r30.b3, 0x01 ; rx_en0 (bit24) +\t.elseif ${channel} == 1 +\tldi r30.b3, 0x02 ; rx_en1 (bit25) +\t.else +\tldi r30.b3, 0x04 ; rx_en2 (bit26) +\t.endif + +`; + + // Bit accumulation loop — channel bits resolved via assembler .if + if (isExtended) { + macroBody += `\tldi ${dataByteRegLo}, 0 +\tldi ${dataByteRegHi}, 0 +\tldi ${bitCounterReg}, 0 + +rx_bit_loop?: +\t; Poll valid flag for selected channel +\t.if ${channel} == 0 +\tqbbc rx_bit_loop?, r31, 24 ; val0 — CH0 +\tmov ${tempSampleReg}, r31.b0 ; rx_data_out0 +\tset r31, r31, 24 ; clear val0 +\t.elseif ${channel} == 1 +\tqbbc rx_bit_loop?, r31, 25 ; val1 — CH1 +\tmov ${tempSampleReg}, r31.b1 ; rx_data_out1 +\tset r31, r31, 25 ; clear val1 +\t.else +\tqbbc rx_bit_loop?, r31, 26 ; val2 — CH2 +\tmov ${tempSampleReg}, r31.b2 ; rx_data_out2 +\tset r31, r31, 26 ; clear val2 +\t.endif + +\tqbbs set_bit?, ${tempSampleReg}, ${middleBit} +`; + if (isLSB) { + macroBody += `\tlsr ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tqbbc no_carry?, ${dataByteRegHi}, 0 +\tset ${dataByteRegLo}, ${dataByteRegLo}, 31 +no_carry?: +\tlsr ${dataByteRegHi}, ${dataByteRegHi}, 1 +\tqba next_bit? +set_bit?: +\tlsr ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tqbbc no_carry_set?, ${dataByteRegHi}, 0 +\tset ${dataByteRegLo}, ${dataByteRegLo}, 31 +no_carry_set?: +\tlsr ${dataByteRegHi}, ${dataByteRegHi}, 1 +\tset ${dataByteRegHi}, ${dataByteRegHi}, 31 +`; + } else { + macroBody += `\tlsl ${dataByteRegHi}, ${dataByteRegHi}, 1 +\tqbbc no_carry?, ${dataByteRegLo}, 31 +\tset ${dataByteRegHi}, ${dataByteRegHi}, 0 +no_carry?: +\tlsl ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tqba next_bit? +set_bit?: +\tlsl ${dataByteRegHi}, ${dataByteRegHi}, 1 +\tqbbc no_carry_set?, ${dataByteRegLo}, 31 +\tset ${dataByteRegHi}, ${dataByteRegHi}, 0 +no_carry_set?: +\tlsl ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tset ${dataByteRegLo}, ${dataByteRegLo}, 0 +`; + } + macroBody += `next_bit?: +\tadd ${bitCounterReg}, ${bitCounterReg}, 1 +\tqbgt rx_bit_loop?, ${bitCounterReg}, ${frameSize} + +`; + } else { + macroBody += `\tldi ${bitAccumReg}, 0 +\tldi ${bitCounterReg}, 0 + +rx_bit_loop?: +\t; Poll valid flag for selected channel +\t.if ${channel} == 0 +\tqbbc rx_bit_loop?, r31, 24 ; val0 — CH0 +\tmov ${tempSampleReg}, r31.b0 ; rx_data_out0 +\tset r31, r31, 24 ; clear val0 +\t.elseif ${channel} == 1 +\tqbbc rx_bit_loop?, r31, 25 ; val1 — CH1 +\tmov ${tempSampleReg}, r31.b1 ; rx_data_out1 +\tset r31, r31, 25 ; clear val1 +\t.else +\tqbbc rx_bit_loop?, r31, 26 ; val2 — CH2 +\tmov ${tempSampleReg}, r31.b2 ; rx_data_out2 +\tset r31, r31, 26 ; clear val2 +\t.endif + +\tqbbs set_bit?, ${tempSampleReg}, ${middleBit} +`; + if (isLSB) { + macroBody += `\tlsr ${bitAccumReg}, ${bitAccumReg}, 1 +\tqba next_bit? +set_bit?: +\tlsr ${bitAccumReg}, ${bitAccumReg}, 1 +\tset ${bitAccumReg}, ${bitAccumReg}, 31 +`; + } else { + macroBody += `\tlsl ${bitAccumReg}, ${bitAccumReg}, 1 +\tqba next_bit? +set_bit?: +\tlsl ${bitAccumReg}, ${bitAccumReg}, 1 +\tset ${bitAccumReg}, ${bitAccumReg}, 0 +`; + } + macroBody += `next_bit?: +\tadd ${bitCounterReg}, ${bitCounterReg}, 1 +\tqbgt rx_bit_loop?, ${bitCounterReg}, ${frameSize} + +`; + } + + // Extraction — identical to original uart_rx + if (isExtended) { + if (isLSB) { + macroBody += `\t; Extract data (extended LSB): shift to align, remove start bit +\tldi TEMP_REG2.w0, 64 +\tsub TEMP_REG2.w0, TEMP_REG2.w0, ${frameSize} +shift_loop?: +\tqbeq shift_done?, TEMP_REG2.w0, 0 +\tlsr ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tqbbc no_shift_carry?, ${dataByteRegHi}, 0 +\tset ${dataByteRegLo}, ${dataByteRegLo}, 31 +no_shift_carry?: +\tlsr ${dataByteRegHi}, ${dataByteRegHi}, 1 +\tsub TEMP_REG2.w0, TEMP_REG2.w0, 1 +\tqba shift_loop? +shift_done?: +\tlsr ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tqbbc no_start_carry?, ${dataByteRegHi}, 0 +\tset ${dataByteRegLo}, ${dataByteRegLo}, 31 +no_start_carry?: +\tlsr ${dataByteRegHi}, ${dataByteRegHi}, 1 +\t.if ${frameSize} == 33 +\tclr ${dataByteRegLo}, ${dataByteRegLo}, 31 +\tldi ${dataByteRegHi}, 0 +\t.elseif ${frameSize} == 34 +\tldi ${dataByteRegHi}, 0 +\t.else +\tldi TEMP_REG2, 1 +\tlsl TEMP_REG2, TEMP_REG2, (${frameSize} - 34) +\tsub TEMP_REG2, TEMP_REG2, 1 +\tand ${dataByteRegHi}, ${dataByteRegHi}, TEMP_REG2 +\t.endif +`; + } else { + macroBody += `\t; Extract data (extended MSB): remove stop bit, mask data bits +\tlsr ${dataByteRegLo}, ${dataByteRegLo}, 1 +\tqbbc no_stop_carry?, ${dataByteRegHi}, 0 +\tset ${dataByteRegLo}, ${dataByteRegLo}, 31 +no_stop_carry?: +\tlsr ${dataByteRegHi}, ${dataByteRegHi}, 1 +\t.if ${frameSize} == 33 +\tclr ${dataByteRegLo}, ${dataByteRegLo}, 31 +\tldi ${dataByteRegHi}, 0 +\t.elseif ${frameSize} == 34 +\tldi ${dataByteRegHi}, 0 +\t.else +\tldi TEMP_REG2, 1 +\tlsl TEMP_REG2, TEMP_REG2, (${frameSize} - 34) +\tsub TEMP_REG2, TEMP_REG2, 1 +\tand ${dataByteRegHi}, ${dataByteRegHi}, TEMP_REG2 +\t.endif +`; + } + } else { + if (isLSB) { + macroBody += `\t; Extract data (standard LSB): shift to align, remove start bit, mask +\tldi TEMP_REG2.w0, 32 +\tsub TEMP_REG2.w0, TEMP_REG2.w0, ${frameSize} +\tlsr ${bitAccumReg}, ${bitAccumReg}, TEMP_REG2.w0 +\tlsr ${dataByteRegLo}, ${bitAccumReg}, 1 +\tldi TEMP_REG2, 1 +\tlsl TEMP_REG2, TEMP_REG2, (${frameSize} - 2) +\tsub TEMP_REG2, TEMP_REG2, 1 +\tand ${dataByteRegLo}, ${dataByteRegLo}, TEMP_REG2 +`; + } else { + macroBody += `\t; Extract data (standard MSB): remove stop bit, mask data bits +\tlsr ${dataByteRegLo}, ${bitAccumReg}, 1 +\tldi TEMP_REG2, 1 +\tlsl TEMP_REG2, TEMP_REG2, (${frameSize} - 2) +\tsub TEMP_REG2, TEMP_REG2, 1 +\tand ${dataByteRegLo}, ${dataByteRegLo}, TEMP_REG2 +`; + } + } + + macroBody += ` +\t; ========== Disable rx_en ========== +\tldi r30.b3, 0x00 +`; + + if (pruInstructionMacro === "") { + macroBody += "\n .endm"; + } + + return macroBody; +} + +function getLongDescription() { + return ` +## UART RX Op (Per-Reception Operation) + +### Purpose +Receives one UART frame using the ENDAT peripheral. This block handles only the +per-reception work: assert rx_en, poll and accumulate all frameSize bits, +extract data, de-assert rx_en. **The \`UART Config\` block (with RX Config enabled) +must appear earlier in the control flow** to set up the peripheral registers before +this block is called. + +### Generated Sequence +1. Assert rx_en for the configured channel (R30[26/25/24] for CH2/CH1/CH0) +2. Loop frameSize times: poll valid flag (R31[26/25/24]), read middle oversample bit from R31 byte, clear flag, accumulate into output register +3. Extract data — shift to align, remove start and stop bits, mask to data width +4. De-assert rx_en (R30.b3 = 0x00) + +### No Peripheral Register Writes +No GPCFG, RXCFG, or CH_CFG0 writes — those are handled once by \`UART Config\`. +This means the op block is fast and suitable for repeated calls in a loop. + +### Output Port +The output register size depends on the configured RX Frame Size: + +| Frame Size | Data Bits | Output Port Type | Register(s) | +|------------|-----------|------------------|-------------| +| 3–32 bits | 1–30 bits | **32-bit** (output32) | 1 register (Rx) | +| 33–64 bits | 31–62 bits | **64-bit** (output64) | 2 registers (Rx:Rx+1) | + +- **output1** (32-bit mode): received data word in a single dynamically allocated register +- **output1** (64-bit mode): received data in two consecutive registers — lower 32 bits in the allocated register, upper bits in the next register + +### Configuration +All parameters are automatically read from the paired \`UART Config\` block — no +duplicate settings needed here. Only \`RX Frame Size\` is set on this block directly, +since it determines the output port type (32-bit vs 64-bit) which the register +allocator needs to know at design time. + +### RX Frame Size +- Total bits per frame = data bits + 2 (start bit + stop bit) +- Standard UART 8-bit: frameSize = 10 +- 16-bit payload: frameSize = 18 +- frameSize > 32 activates extended mode (two output registers, 64-bit output port) + +### Calculation Example +- UART Config: 192 MHz clock, 12 MHz baud, 8x oversample +- RX Clock = 192 / (clockDiv+1) = 192 / 2 = 96 MHz +- Baud Rate = 96 MHz / 8 = 12 MHz ✓ +- frameSize = 18 → 16 data bits → 32-bit output port (fits in one register) + +### Terminology +- **Valid Flag**: R31 status bit set by hardware when an oversampled bit is ready to read +- **Middle Sample**: For 8x oversample the middle sample is bit 4 of the 8-sample window — most noise-immune point +- **Extended Mode**: frameSize ≥ 33 → data bits ≥ 31 → requires two consecutive output registers +- **rx_en**: R30[26:24] — enables reception on the selected channel; asserting it starts the hardware + +--- +`; +} + +function getAIContext() { + return getLongDescription() + ` +## How to Configure (For AI/Scripting) + +This section describes how to programmatically configure the UART RX Op block in a .syscfg file. + +**CRITICAL**: The UART RX Op block requires a paired \`UART Config\` block with RX enabled. +The op block reads all RX parameters (channel, baud rate, oversample, bit order, start bit polarity) +from the config block automatically. Only \`rxFrameSize\` is set on the op block itself. + +### Step 1 — Add and configure a UART Config block (RX enabled) + +\`\`\`javascript +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +uart_config1.$name = "PRU_UART_CONFIG_0"; +uart_config1.enableTX = false; // TX-only or TX+RX — set as needed +uart_config1.enableRX = true; +uart_config1.rxChannel = 2; // Channel 2 (GPI11 = PERIF2_IN) +uart_config1.rxClockSource = 0; // 192 MHz (UART_CLK) +uart_config1.rxBaudRate = 12; // 12 MHz +uart_config1.rxOversampleSize = 7; // 8x oversample +uart_config1.rxStartBitPolarity = 1; // Rising edge +uart_config1.rxBitSwap = true; // LSB first (standard UART) +\`\`\` + +### Step 2 — Add and configure the UART RX Op block + +\`\`\`javascript +const uart_rx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx_op", {}, false); +const uart_rx_op1 = uart_rx_op.addInstance(); +uart_rx_op1.$name = "PRU_UART_RX_OP_0"; +uart_rx_op1.rxFrameSize = 18; // 16 data bits + start + stop = 18 +uart_rx_op1.uartConfig = uart_config1; // Link to config block +\`\`\` + +### Step 3 — Connect control flow and data + +\`\`\`javascript +// Control flow: config must run before op +scripting.connect(uart_config1, "next", uart_rx_op1, "prev"); + +// Data: connect op output to downstream block +scripting.connect(uart_rx_op1, "output1", next_block, "input1"); + +// Control flow out of op +scripting.connect(uart_rx_op1, "next", next_block, "prev"); +\`\`\` + +### Configuration Parameters + +| Parameter | Type | Valid Values | Default | Description | +|-----------|------|--------------|---------|-------------| +| rxFrameSize | Integer | 3–64 | 10 | Total frame bits (dataBits + 2). Values > 32 use 64-bit output port | + +All other RX parameters (channel, baud rate, oversample, bit order, start bit polarity) are +read from the paired \`UART Config\` block — do not duplicate them here. + +### Output Port Type by Frame Size + +| rxFrameSize | Data Bits | Output Port | Notes | +|-------------|-----------|-------------|-------| +| 3–32 | 1–30 | 32-bit (output32) | Single register output | +| 33–64 | 31–62 | 64-bit (output64) | Two consecutive registers; downstream block must accept 64-bit input | + +### Common Frame Sizes + +| Protocol | Data Bits | rxFrameSize | +|----------|-----------|-------------| +| Standard UART 8-bit | 8 | 10 | +| UART 16-bit payload | 16 | 18 | +| UART 24-bit payload | 24 | 26 | +| UART 30-bit payload | 30 | 32 | +| UART 31-bit payload (extended) | 31 | 33 | + +### Important Notes + +1. **UART Config must appear before UART RX Op** in the control flow (connect config "next" to op "prev"). + +2. **rxFrameSize > 32 activates extended mode** — output port becomes 64-bit. Downstream blocks must be wired to accept a 64-bit input. + +3. **No peripheral register writes in this block** — only rx_en assert/de-assert and the bit polling loop. All register setup is in \`UART Config\`. + +4. **uartConfig linkage is mandatory** — always set \`uart_rx_op1.uartConfig = uart_config1\` or the block will error during validation. +`; +} + +exports = { + displayName: "PRU UART RX Op", + defaultInstanceName: "PRU_UART_RX_OP_", + longDescription: getLongDescription(), + getAIContext: getAIContext, + uiView: "graph", + templates: { + "/pru_blocks/common/pru_syscfg.asm.xdt": null + }, + config: [ + { + name: "$shape", + hidden: true, + default: "square", + }, + { + name: "$topLabel", + hidden: true, + default: "", + }, + // Local rxFrameSize — source of truth for ports() and output1Size + // uart_config reads this via getRxFrameSize() instead of having its own field + { + name: "rxFrameSize", + displayName: "RX Frame Size (bits)", + description: "Total bits per RX frame = dataBits + 2 (start + stop). Range: 3-64. Values > 32 use extended mode (two output registers).", + default: 10, + range: [3, 64], + displayFormat: "dec" + }, + // Read-only display of config block settings + { + name: "configInfo", + displayName: "Config Block RX Settings", + description: "Parameters read from the UART Config block (RX section)", + default: "No config block found", + readOnly: true, + getValue: (inst) => { + const cfg = getConfigInst(inst); + if (!cfg) return "No UART Config block found"; + if (!cfg.enableRX) return "UART Config block has RX disabled"; + return `Ch${cfg.rxChannel}, ${inst.rxFrameSize}b frame, ` + + `${cfg.rxBitSwap ? "LSB" : "MSB"}, start=${cfg.rxStartBitPolarity}, ` + + `${cfg.rxBaudRate}MHz, ` + + `${["1x","2x","","4x","","","","8x"][cfg.rxOversampleSize]}OS`; + } + }, + // ===== HIDDEN CODE GEN FIELDS ===== + { + name: "constant1", + hidden: true, + default: "2, 1, 0, 7, 1, 10, 1", + getValue: (inst) => { + const cfg = getConfigInst(inst); + if (!cfg) return "2, 1, 0, 7, 1, 10, 1"; + let osMul; + if (cfg.rxOversampleSize === 0) osMul = 1; + else if (cfg.rxOversampleSize === 1) osMul = 2; + else if (cfg.rxOversampleSize === 3) osMul = 4; + else osMul = 8; + const clkMHz = cfg.rxClockSource === 0 ? 192 : 200; + const bxo = cfg.rxBaudRate * osMul; + const rxDiv = (bxo === 0 || clkMHz % bxo !== 0) ? 0 : (clkMHz / bxo) - 1; + return `${cfg.rxChannel}, ${rxDiv}, ${cfg.rxClockSource}, ` + + `${cfg.rxOversampleSize}, ${cfg.rxStartBitPolarity}, ${inst.rxFrameSize}, ` + + `${cfg.rxBitSwap ? 1 : 0}`; + } + }, + { + name: "opCode", + displayName: "m_uart_rx_op", + default: "m_uart_rx_op", + hidden: true, + getValue: (inst) => { + const cfg = getConfigInst(inst); + const pruNum = cfg ? cfg.pruSelect : 0; + const bitOrder = cfg ? (cfg.rxBitSwap ? "lsb" : "msb") : "lsb"; + const dataBits = inst.rxFrameSize - 2; + const isExt = dataBits >= 31; + let osName; + const osEnc = cfg ? cfg.rxOversampleSize : 7; + if (osEnc === 0) osName = "1x"; + else if (osEnc === 1) osName = "2x"; + else if (osEnc === 3) osName = "4x"; + else osName = "8x"; + const extSuffix = isExt ? "_ext" : ""; + return `m_uart_rx_op_pru${pruNum}_${bitOrder}_${osName}${extSuffix}`; + } + }, + { + name: "numOfInputPorts", + default: 0, + hidden: true + }, + { + name: "numOfOutputPorts", + default: 1, + hidden: true + }, + { + name: "output1Size", + hidden: true, + default: 4, + getValue: (inst) => { + const dataBits = inst.rxFrameSize - 2; + return dataBits >= 31 ? 8 : 4; + } + }, + { + name: "numOfConstants", + default: 1, + hidden: true + }, + { + name: "noOfCycles", + displayName: "Number Of Cycles", + getValue: (inst) => { + const frameSize = inst.rxFrameSize; + // rx_en assert(1) + loop(frameSize * ~5) + extract(~8) + disable(1) + return 10 + (frameSize * 5); + }, + default: 60 + }, + ], + ports: (inst) => { + const dataBits = inst.rxFrameSize - 2; + const outputType = dataBits >= 31 ? "output64" : "output32"; + return [ + { name: "output1", type: outputType }, + { name: "prev", type: "PREV" }, + { name: "next", type: "NEXT" }, + ]; + }, + validate, + sharedModuleInstances: (_inst) => { + return [ + { + name: "uartConfig", + displayName: "UART Config", + moduleName: "/pru_blocks/pru_io_blocks/uart_config", + collapsed: false, + } + ]; + }, + moduleStatic: { + modules: function(inst) { + return [{ + name: "pru_register_allocator_validator", + moduleName: "/pru_blocks/common/pru_blocks_static_module" + }] + }, + }, + getMacro +} diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx_op.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx_op.syscfg.js new file mode 100644 index 0000000..5f9a747 --- /dev/null +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx_op.syscfg.js @@ -0,0 +1,989 @@ +/** + * Single-shot TX operation (dataBits 1-29). + * Assumes peripheral is already configured by uart_tx_config block. + * Only does: frame construction + FIFO load + start + wait TX done. + */ +function getMacroSingleShot(pruInstructionMacro, opCode) { + let macroBody = ""; + + const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); + const macroName = parts[0] || opCode; + const dataRegLo = parts[1] || "dataRegLo"; + const channel = parts[2] || "channel"; + const dataBits = parts[3] || "dataBits"; + + const isLSB = macroName.includes("_lsb_"); + const startBit1 = macroName.includes("_start1"); + + const hasExplicitStop0 = macroName.includes("_stop0"); + const hasExplicitStop1 = macroName.includes("_stop1"); + const stopBitPol = hasExplicitStop0 ? "0" : (hasExplicitStop1 ? "1" : (startBit1 ? "0" : "1")); + const startBitPol = startBit1 ? "1" : "0"; + + const numBytesMatch = macroName.match(/_(\d)byte_/); + const numFifoBytes = numBytesMatch ? parseInt(numBytesMatch[1]) : 2; + + if (pruInstructionMacro === "") { + macroBody = macroName + "\t" + ".macro " + dataRegLo + ", " + channel + ", " + dataBits + "\n"; + macroBody += `\t; Parameters: +\t; dataRegLo - Register containing data (up to 29 bits) +\t; channel - ENDAT channel (0, 1, or 2) - must match uart_tx_config channel +\t; dataBits - Number of data bits to transmit (1-29) +\t; Configuration: ${isLSB ? "LSB" : "MSB"} first, start bit=${startBitPol}, stop bit=${stopBitPol} +\t; Mode: Single-shot (frame size <= 31 bits), FIFO loads: ${numFifoBytes} bytes +\t; NOTE: uart_tx_config block must be called before this block to set up the peripheral +`; + } + + macroBody += ` +\t; ========== Per-command Reinit (reinit BEFORE de-asserting rx_en — TRM order) ========== +\t; Clears FIFO, resets state machines, handles stuck rx_en from a previous incomplete RX. +\tset r31, r31, 19 ; 1. Global Reinit FIRST +\t.if ${channel} == 0 +wait_reinit?: +\tqbbs wait_reinit?, r31, 5 ; 2. Wait — CH0 busy bit +\t.elseif ${channel} == 1 +wait_reinit?: +\tqbbs wait_reinit?, r31, 13 ; 2. Wait — CH1 busy bit +\t.else +wait_reinit?: +\tqbbs wait_reinit?, r31, 21 ; 2. Wait — CH2 busy bit +\t.endif +\tldi r30.b3, 0x00 ; 3. De-assert rx_en AFTER reinit + +\t; ========== Select Channel + clk_mode=1 ========== +\t; clk_mode=1: free-running, stops HIGH after last RX frame. +\t; Keeps clock running after TX so the encoder can clock its response back. +\t.if ${channel} == 0 +\tldi r30.w2, 0x0008 ; clk_mode=1 (bits[20:19]=0b01), ch=0 +\t.elseif ${channel} == 1 +\tldi r30.w2, 0x0009 ; clk_mode=1, ch=1 +\t.else +\tldi r30.w2, 0x000A ; clk_mode=1, ch=2 +\t.endif + +\t; ========== Construct Frame ========== +\tmov TEMP_REG1, ${dataRegLo} +`; + + if (isLSB) { + if (stopBitPol === "1") { + macroBody += `\tset TEMP_REG1, TEMP_REG1, ${dataBits} ; Set stop bit (will be at dataBits+1 after shift) +`; + } else { + macroBody += `\tclr TEMP_REG1, TEMP_REG1, ${dataBits} ; Clear stop bit (will be at dataBits+1 after shift) +`; + } + macroBody += `\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift left for start bit at position 0 +`; + if (startBitPol === "1") { + macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set start bit at position 0 +`; + } + } else { + macroBody += `\tlsl TEMP_REG1, TEMP_REG1, (31 - ${dataBits}) ; Align data to MSB +`; + if (startBitPol === "1") { + macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set start bit at position 31 +`; + } else { + macroBody += `\tclr TEMP_REG1, TEMP_REG1, 31 ; Clear start bit at position 31 +`; + } + if (stopBitPol === "1") { + macroBody += `\tset TEMP_REG1, TEMP_REG1, (31 - ${dataBits} - 1) ; Set stop bit +`; + } else { + macroBody += `\tclr TEMP_REG1, TEMP_REG1, (31 - ${dataBits} - 1) ; Clear stop bit +`; + } + } + + macroBody += ` +\t; ========== Load FIFO ========== +`; + if (isLSB) { + macroBody += `\tmov r30.b0, TEMP_REG1.b0\n`; + if (numFifoBytes >= 2) macroBody += `\tmov r30.b0, TEMP_REG1.b1\n`; + if (numFifoBytes >= 3) macroBody += `\tmov r30.b0, TEMP_REG1.b2\n`; + if (numFifoBytes >= 4) macroBody += `\tmov r30.b0, TEMP_REG1.b3\n`; + } else { + macroBody += `\tmov r30.b0, TEMP_REG1.b3\n`; + if (numFifoBytes >= 2) macroBody += `\tmov r30.b0, TEMP_REG1.b2\n`; + if (numFifoBytes >= 3) macroBody += `\tmov r30.b0, TEMP_REG1.b1\n`; + if (numFifoBytes >= 4) macroBody += `\tmov r30.b0, TEMP_REG1.b0\n`; + } + + macroBody += ` +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +\t; ========== Wait for TX Complete ========== +\t.if ${channel} == 0 +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 5 +\t.elseif ${channel} == 1 +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 13 +\t.else +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 21 +\t.endif +`; + + if (pruInstructionMacro === "") { + macroBody += "\n .endm"; + } + return macroBody; +} + +/** + * Specific-bit TX operation for 30, 31, 32 bits. + * Mirrors getMacroSpecificBits from uart_tx.syscfg.js but without config writes. + */ +function getMacroSpecificBits(pruInstructionMacro, opCode, bitSize) { + let macroBody = ""; + + const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); + const macroName = parts[0] || opCode; + const dataRegLo = parts[1] || "dataRegLo"; + const channel = parts[2] || "channel"; + + const isLSB = macroName.includes("_lsb_"); + const startBit1 = macroName.includes("_start1"); + + const hasExplicitStop0 = macroName.includes("_stop0"); + const hasExplicitStop1 = macroName.includes("_stop1"); + const stopBitPol = hasExplicitStop0 ? "0" : (hasExplicitStop1 ? "1" : (startBit1 ? "0" : "1")); + const startBitPol = startBit1 ? "1" : "0"; + + const frameSize = bitSize + 2; + const numFifoBytes = Math.ceil(frameSize / 8); + + if (pruInstructionMacro === "") { + macroBody = macroName + "\t" + ".macro " + dataRegLo + ", " + channel + "\n"; + macroBody += `\t; Parameters: +\t; dataRegLo - Register containing ${bitSize} bits of data +\t; channel - ENDAT channel (0, 1, or 2) - must match uart_tx_config channel +\t; Configuration: ${isLSB ? "LSB" : "MSB"} first, start bit=${startBitPol}, stop bit=${stopBitPol} +\t; Mode: Continuous (${bitSize}-bit specific), frame size = ${frameSize} bits, ${numFifoBytes} bytes +\t; NOTE: uart_tx_config block must be called before this block to set up the peripheral +`; + } + + macroBody += ` +\t; ========== Per-command Reinit (reinit BEFORE de-asserting rx_en — TRM order) ========== +\tset r31, r31, 19 ; 1. Global Reinit FIRST +\t.if ${channel} == 0 +wait_reinit?: +\tqbbs wait_reinit?, r31, 5 ; 2. Wait — CH0 busy bit +\t.elseif ${channel} == 1 +wait_reinit?: +\tqbbs wait_reinit?, r31, 13 ; 2. Wait — CH1 busy bit +\t.else +wait_reinit?: +\tqbbs wait_reinit?, r31, 21 ; 2. Wait — CH2 busy bit +\t.endif +\tldi r30.b3, 0x00 ; 3. De-assert rx_en AFTER reinit + +\t; ========== Select Channel + clk_mode=1 ========== +\t.if ${channel} == 0 +\tldi r30.w2, 0x0008 ; clk_mode=1 (bits[20:19]=0b01), ch=0 +\t.elseif ${channel} == 1 +\tldi r30.w2, 0x0009 ; clk_mode=1, ch=1 +\t.else +\tldi r30.w2, 0x000A ; clk_mode=1, ch=2 +\t.endif + +`; + + if (isLSB) { + if (bitSize === 30) { + macroBody += ` +\t; ========== Construct Frame (LSB First, 30-bit) ========== +\t; Frame: start@0, data[29:0]@[30:1], stop@31 — total 32 bits = 4 bytes +\tmov TEMP_REG1, ${dataRegLo} +\tlsl TEMP_REG1, TEMP_REG1, 1 +`; + if (startBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + if (stopBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 31\n`; + else macroBody += `\tclr TEMP_REG1, TEMP_REG1, 31\n`; + macroBody += ` +\t; ========== Load 4 Bytes to FIFO (LSB First) ========== +\tmov r30.b0, TEMP_REG1.b0 +\tmov r30.b0, TEMP_REG1.b1 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b3 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +`; + } else if (bitSize === 31) { + macroBody += ` +\t; ========== Construct Frame (LSB First, 31-bit) ========== +\t; Frame: start@0, data[30:0]@[31:1], stop@32 — total 33 bits = 5 bytes +\tmov TEMP_REG1, ${dataRegLo} +\tldi TEMP_REG2, 0 +\tlsl TEMP_REG1, TEMP_REG1, 1 +`; + if (startBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + if (stopBitPol === "1") macroBody += `\tset TEMP_REG2, TEMP_REG2, 0\n`; + macroBody += ` +\t; ========== Load Initial 4 Bytes to FIFO (LSB First) ========== +\tmov r30.b0, TEMP_REG1.b0 +\tmov r30.b0, TEMP_REG1.b1 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b3 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +\t; ========== Load Byte 5 via Continuous Polling ========== +wait_fifo_for_byte5?: +\tmov ${dataRegLo}, r31 +\tlsr ${dataRegLo}, ${dataRegLo}, 2 +\tand ${dataRegLo}, ${dataRegLo}, 0x7 +\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 +\tmov r30.b0, TEMP_REG2.b0 + +`; + } else if (bitSize === 32) { + macroBody += ` +\t; ========== Construct Frame (LSB First, 32-bit) ========== +\t; Frame: start@0, data[30:0]@[31:1], data[31]@32, stop@33 — total 34 bits = 5 bytes +\tmov TEMP_REG1, ${dataRegLo} +\tlsr TEMP_REG2, TEMP_REG1, 31 +\tor TEMP_REG2, TEMP_REG2, 0xFC +\tlsl TEMP_REG1, TEMP_REG1, 1 +`; + if (startBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + if (stopBitPol === "1") macroBody += `\tset TEMP_REG2, TEMP_REG2, 1\n`; + else macroBody += `\tclr TEMP_REG2, TEMP_REG2, 1\n`; + macroBody += ` +\t; ========== Load Initial 4 Bytes to FIFO (LSB First) ========== +\tmov r30.b0, TEMP_REG1.b0 +\tmov r30.b0, TEMP_REG1.b1 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b3 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +\t; ========== Load Byte 5 via Continuous Polling ========== +wait_fifo_for_byte5?: +\tmov ${dataRegLo}, r31 +\tlsr ${dataRegLo}, ${dataRegLo}, 2 +\tand ${dataRegLo}, ${dataRegLo}, 0x7 +\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 +\tmov r30.b0, TEMP_REG2.b0 + +`; + } + } else { + // MSB mode 30/31/32 — mirrors uart_tx.syscfg.js getMacroSpecificBits MSB section + if (bitSize === 30) { + macroBody += ` +\t; ========== Construct Frame (MSB First, 30-bit) ========== +\t; Frame: start@31, data[29:0]@[30:1], stop@0 — total 32 bits = 4 bytes +\tmov TEMP_REG1, ${dataRegLo} +\tlsl TEMP_REG1, TEMP_REG1, 1 +`; + if (startBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 31\n`; + if (stopBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + macroBody += ` +\t; ========== Load 4 Bytes to FIFO (MSB First) ========== +\tmov r30.b0, TEMP_REG1.b3 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b1 +\tmov r30.b0, TEMP_REG1.b0 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +`; + } else if (bitSize === 31) { + macroBody += ` +\t; ========== Construct Frame (MSB First, 31-bit) ========== +\t; Frame: start@32 (TEMP_REG2 bit 0), data[30:0]@[31:1], stop@0 — total 33 bits = 5 bytes +`; + if (startBitPol === "0") { + macroBody += `\tldi TEMP_REG2, 0xFE\n`; + } else { + macroBody += `\tldi TEMP_REG2, 0\n\tset TEMP_REG2, TEMP_REG2, 0\n`; + } + macroBody += `\tmov TEMP_REG1, ${dataRegLo} +\tlsl TEMP_REG1, TEMP_REG1, 1 +`; + if (stopBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + macroBody += ` +\t; ========== Load Initial 4 Bytes to FIFO (MSB First) ========== +\tmov r30.b0, TEMP_REG2.b0 +\tmov r30.b0, TEMP_REG1.b3 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b1 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +\t; ========== Load Byte 5 via Continuous Polling ========== +wait_fifo_for_byte5?: +\tmov ${dataRegLo}, r31 +\tlsr ${dataRegLo}, ${dataRegLo}, 2 +\tand ${dataRegLo}, ${dataRegLo}, 0x7 +\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 +\tmov r30.b0, TEMP_REG1.b0 + +`; + } else if (bitSize === 32) { + macroBody += ` +\t; ========== Construct Frame (MSB First, 32-bit) ========== +\t; Frame: start@33 (TEMP_REG2 bit 1), data[31]@32 (TEMP_REG2 bit 0), data[30:0]@[31:1], stop@0 +\t; Total: 34 bits = 5 bytes +`; + if (startBitPol === "0") { + macroBody += `\tldi TEMP_REG2, 0xFC\n`; + } else { + macroBody += `\tldi TEMP_REG2, 0\n\tset TEMP_REG2, TEMP_REG2, 1\n`; + } + macroBody += `\tmov TEMP_REG1, ${dataRegLo} +\tlsr ${dataRegLo}, ${dataRegLo}, 31 +\tor TEMP_REG2, TEMP_REG2, ${dataRegLo} +\tlsl TEMP_REG1, TEMP_REG1, 1 +`; + if (stopBitPol === "1") macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + macroBody += ` +\t; ========== Load Initial 4 Bytes to FIFO (MSB First) ========== +\tmov r30.b0, TEMP_REG2.b0 +\tmov r30.b0, TEMP_REG1.b3 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b1 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +\t; ========== Load Byte 5 via Continuous Polling ========== +wait_fifo_for_byte5?: +\tmov ${dataRegLo}, r31 +\tlsr ${dataRegLo}, ${dataRegLo}, 2 +\tand ${dataRegLo}, ${dataRegLo}, 0x7 +\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 +\tmov r30.b0, TEMP_REG1.b0 + +`; + } + } + + // Wait for FIFO empty then TX complete for 31/32 bit cases + if (bitSize >= 31) { + macroBody += `\t; ========== Wait for FIFO Empty ========== +wait_fifo_empty?: +\tmov TEMP_REG1, r31 +\tlsr TEMP_REG1, TEMP_REG1, 2 +\tand TEMP_REG1, TEMP_REG1, 0x7 +\tqbne wait_fifo_empty?, TEMP_REG1, 0 + +`; + } + + macroBody += `\t; ========== Wait for TX Complete ========== +\t.if ${channel} == 0 +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 5 +\t.elseif ${channel} == 1 +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 13 +\t.else +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 21 +\t.endif +`; + + if (pruInstructionMacro === "") { + macroBody += "\n .endm"; + } + return macroBody; +} + +/** + * Continuous TX operation (dataBits 33-62). + * Mirrors getMacroContinuous from uart_tx.syscfg.js but without config writes. + */ +function getMacroContinuous(pruInstructionMacro, opCode) { + let macroBody = ""; + + const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); + const macroName = parts[0] || opCode; + const dataRegLo = parts[1] || "dataRegLo"; + const dataRegHi = parts[2] || "dataRegHi"; + const channel = parts[3] || "channel"; + const dataBits = parts[4] || "dataBits"; + + const isLSB = macroName.includes("_lsb_"); + const startBit1 = macroName.includes("_start1"); + + const hasExplicitStop0 = macroName.includes("_stop0"); + const hasExplicitStop1 = macroName.includes("_stop1"); + const stopBitPol = hasExplicitStop0 ? "0" : (hasExplicitStop1 ? "1" : (startBit1 ? "0" : "1")); + const startBitPol = startBit1 ? "1" : "0"; + + const numBytesMatch = macroName.match(/_(\d)byte_/); + const numFifoBytes = numBytesMatch ? parseInt(numBytesMatch[1]) : 5; + const continuousBytes = numFifoBytes - 4; + + if (pruInstructionMacro === "") { + macroBody = macroName + "\t" + ".macro " + dataRegLo + ", " + dataRegHi + ", " + channel + ", " + dataBits + "\n"; + macroBody += `\t; Parameters: +\t; dataRegLo - Register containing lower 32 bits of data +\t; dataRegHi - Register containing upper bits of data (next consecutive register) +\t; channel - ENDAT channel (0, 1, or 2) - must match uart_tx_config channel +\t; dataBits - Number of data bits to transmit (33-62) +\t; Configuration: ${isLSB ? "LSB" : "MSB"} first, start bit=${startBitPol}, stop bit=${stopBitPol} +\t; Mode: Continuous, total bytes = ${numFifoBytes} (initial 4 + continuous ${continuousBytes}) +\t; NOTE: uart_tx_config block must be called before this block to set up the peripheral +`; + } + + macroBody += ` +\t; ========== Per-command Reinit (reinit BEFORE de-asserting rx_en — TRM order) ========== +\tset r31, r31, 19 ; 1. Global Reinit FIRST +\t.if ${channel} == 0 +wait_reinit?: +\tqbbs wait_reinit?, r31, 5 ; 2. Wait — CH0 busy bit +\t.elseif ${channel} == 1 +wait_reinit?: +\tqbbs wait_reinit?, r31, 13 ; 2. Wait — CH1 busy bit +\t.else +wait_reinit?: +\tqbbs wait_reinit?, r31, 21 ; 2. Wait — CH2 busy bit +\t.endif +\tldi r30.b3, 0x00 ; 3. De-assert rx_en AFTER reinit + +\t; ========== Select Channel + clk_mode=1 ========== +\t.if ${channel} == 0 +\tldi r30.w2, 0x0008 ; clk_mode=1 (bits[20:19]=0b01), ch=0 +\t.elseif ${channel} == 1 +\tldi r30.w2, 0x0009 ; clk_mode=1, ch=1 +\t.else +\tldi r30.w2, 0x000A ; clk_mode=1, ch=2 +\t.endif + +`; + + if (isLSB) { + macroBody += ` +\t; ========== Construct Frame (LSB First, Continuous >32-bit) ========== +\tmov TEMP_REG1, ${dataRegLo} +\tmov TEMP_REG2, ${dataRegHi} +\tlsr ${dataRegLo}, TEMP_REG1, 31 +\tlsl TEMP_REG1, TEMP_REG1, 1 +\tlsl TEMP_REG2, TEMP_REG2, 1 +\tor TEMP_REG2, TEMP_REG2, ${dataRegLo} + +`; + if (startBitPol === "1") { + macroBody += `\tset TEMP_REG1, TEMP_REG1, 0\n`; + } + if (stopBitPol === "1") { + macroBody += `\t.if (${dataBits} + 1) < 32 +\tset TEMP_REG1, TEMP_REG1, (${dataBits} + 1) +\t.else +\tset TEMP_REG2, TEMP_REG2, (${dataBits} + 1 - 32) +\t.endif +`; + } else { + macroBody += `\t.if (${dataBits} + 1) < 32 +\tclr TEMP_REG1, TEMP_REG1, (${dataBits} + 1) +\t.else +\tclr TEMP_REG2, TEMP_REG2, (${dataBits} + 1 - 32) +\t.endif +`; + } + macroBody += ` +\t; ========== Load Initial 4 Bytes to FIFO (LSB First) ========== +\tmov r30.b0, TEMP_REG1.b0 +\tmov r30.b0, TEMP_REG1.b1 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b3 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +`; + const byteNamesLSB = ["TEMP_REG2.b0", "TEMP_REG2.b1", "TEMP_REG2.b2", "TEMP_REG2.b3"]; + for (let i = 0; i < continuousBytes; i++) { + const byteNum = 5 + i; + macroBody += `\t; ========== Load Byte ${byteNum} via Continuous Polling ========== +wait_fifo_for_byte${byteNum}?: +\tmov ${dataRegLo}, r31 +\tlsr ${dataRegLo}, ${dataRegLo}, 2 +\tand ${dataRegLo}, ${dataRegLo}, 0x7 +\tqblt wait_fifo_for_byte${byteNum}?, ${dataRegLo}, 2 +\tmov r30.b0, ${byteNamesLSB[i]} + +`; + } + } else { + macroBody += ` +\t; ========== Construct Frame (MSB First, Continuous >32-bit) ========== +\tldi TEMP_REG1, 0 +\tldi TEMP_REG2, 0 +`; + if (startBitPol === "1") { + macroBody += `\tset TEMP_REG1, TEMP_REG1, 31\n`; + } + macroBody += `\tmov TEMP_REG2, ${dataRegHi} +\tlsl TEMP_REG2, TEMP_REG2, (63 - ${dataBits}) +\tor TEMP_REG1, TEMP_REG1, TEMP_REG2 +\tmov TEMP_REG2, ${dataRegLo} +\tlsr TEMP_REG2, TEMP_REG2, (${dataBits} - 31) +\tor TEMP_REG1, TEMP_REG1, TEMP_REG2 +\tmov TEMP_REG2, ${dataRegLo} +\tlsl TEMP_REG2, TEMP_REG2, (63 - ${dataBits}) +`; + if (stopBitPol === "1") { + macroBody += `\tset TEMP_REG2, TEMP_REG2, (62 - ${dataBits})\n`; + } else { + macroBody += `\tclr TEMP_REG2, TEMP_REG2, (62 - ${dataBits})\n`; + } + macroBody += ` +\t; ========== Load Initial 4 Bytes to FIFO (MSB First) ========== +\tmov r30.b0, TEMP_REG1.b3 +\tmov r30.b0, TEMP_REG1.b2 +\tmov r30.b0, TEMP_REG1.b1 +\tmov r30.b0, TEMP_REG1.b0 + +\t; ========== Start Transmission ========== +\tset r31, r31, 18 + +`; + const byteNamesMSB = ["TEMP_REG2.b3", "TEMP_REG2.b2", "TEMP_REG2.b1", "TEMP_REG2.b0"]; + for (let i = 0; i < continuousBytes; i++) { + const byteNum = 5 + i; + macroBody += `\t; ========== Load Byte ${byteNum} via Continuous Polling ========== +wait_fifo_for_byte${byteNum}?: +\tmov TEMP_REG1, r31 +\tlsr TEMP_REG1, TEMP_REG1, 2 +\tand TEMP_REG1, TEMP_REG1, 0x7 +\tqblt wait_fifo_for_byte${byteNum}?, TEMP_REG1, 2 +\tmov r30.b0, ${byteNamesMSB[i]} + +`; + } + } + + macroBody += `\t; ========== Wait for FIFO Empty ========== +wait_fifo_empty?: +\tmov TEMP_REG1, r31 +\tlsr TEMP_REG1, TEMP_REG1, 2 +\tand TEMP_REG1, TEMP_REG1, 0x7 +\tqbne wait_fifo_empty?, TEMP_REG1, 0 + +\t; ========== Wait for TX Complete ========== +\t.if ${channel} == 0 +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 5 +\t.elseif ${channel} == 1 +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 13 +\t.else +wait_tx_done?: +\tqbbs wait_tx_done?, r31, 21 +\t.endif +`; + + if (pruInstructionMacro === "") { + macroBody += "\n .endm"; + } + return macroBody; +} + +function getMacro(pruInstructionMacro, opCode) { + // Check for specific bit-size macros (30, 31, 32 bits) + const bitSizeMatch = opCode.match(/_(\d+)bit_/); + if (bitSizeMatch) { + const bitSize = parseInt(bitSizeMatch[1]); + if (bitSize >= 30 && bitSize <= 32) { + return getMacroSpecificBits(pruInstructionMacro, opCode, bitSize); + } + } + + const numBytesMatch = opCode.match(/_(\d)byte_/); + const numFifoBytes = numBytesMatch ? parseInt(numBytesMatch[1]) : 2; + + if (numFifoBytes <= 4) { + return getMacroSingleShot(pruInstructionMacro, opCode); + } else { + return getMacroContinuous(pruInstructionMacro, opCode); + } +} + +/** + * Helper to get the uart_config instance. + * Only call this from getValue functions, NOT from validate or ports. + */ +function getConfigInst() { + const cfgModule = system.modules["/pru_blocks/pru_io_blocks/uart_config"]; + if (cfgModule && cfgModule.$instances && cfgModule.$instances.length > 0) { + return cfgModule.$instances[0]; + } + return null; +} + +function validate(inst, report) { + const cfgModule = system.modules["/pru_blocks/pru_io_blocks/uart_config"]; + if (!cfgModule || !cfgModule.$instances || cfgModule.$instances.length === 0) { + report.logError( + "A UART Config block is required before using UART TX Op. " + + "Add a UART Config block with TX Config enabled and connect it earlier in the control flow.", + inst + ); + return; + } + const cfg = cfgModule.$instances[0]; + if (!cfg.enableTX) { + report.logError( + "The UART Config block does not have TX Config enabled. " + + "Enable TX Config in the UART Config block.", + inst + ); + } + + for (let i = 1; i <= inst["numOfInputPorts"]; i++) { + if (inst["input" + i.toString()].length === 0) { + report.logWarning("input" + i.toString() + " port not connected to output port", inst); + } + } + + // Enforce all uart_tx_op instances use the same dataBits value + const txOpModule = system.modules["/pru_blocks/pru_io_blocks/uart_tx_op"]; + if (txOpModule && txOpModule.$instances && txOpModule.$instances.length > 1) { + const firstDataBits = txOpModule.$instances[0].dataBits; + const mismatch = txOpModule.$instances.some(i => i.dataBits !== firstDataBits); + if (mismatch) { + report.logError( + `All UART TX Op blocks must have the same Data Bits value. ` + + `uart_config uses the first instance's value (${firstDataBits}b) for TX configuration — ` + + `mismatched instances will generate incorrect assembly.`, + inst, "dataBits" + ); + } + } +} + +function getLongDescription() { + return ` +## UART TX Op (Per-Transmission Operation) + +### Purpose +Transmits one UART frame using the ENDAT peripheral. This block handles only the +per-transmission work: frame construction, FIFO loading, start trigger, and wait for +TX complete. **The \`UART Config\` block (with TX Config enabled) must appear earlier in +the control flow** to set up the peripheral registers before this block is called. + +### Generated Sequence +1. Per-command reinit (R31 bit 19) — clears FIFO and state machines (TRM-mandated order: reinit before de-asserting rx_en) +2. Poll reinit busy bit (R31[5/13/21] for CH0/CH1/CH2) +3. De-assert rx_en (R30.b3 = 0x00) +4. Channel select + clk_mode write to R30.w2 +5. Frame construction — insert start bit, shift data bits, insert stop bit (LSB or MSB order) +6. FIFO load — 1–4 bytes for single-shot (dataBits ≤ 29); 4 bytes + continuous polling for dataBits 30–32; 4 bytes + byte 5 polling for dataBits 33–62 +7. Start transmission (R31 bit 18 = tx_channel_go) +8. Wait for TX complete (poll R31[5/13/21] busy bit) + +### No Peripheral Register Writes +No GPCFG, TXCFG, or CH_CFG0 writes — those are handled once by \`UART Config\`. +This means the op block is suitable for repeated calls in a loop with minimal overhead. + +### Input Port +The input register size depends on the configured Data Bits: + +| Data Bits | Input Port Type | Register(s) | +|-----------|-----------------|-------------| +| 1–32 bits | **32-bit** (input32) | 1 register (Rx) | +| 33–62 bits | **64-bit** (input64) | 2 consecutive registers (Rx:Rx+1) | + +- **input1** (32-bit mode): data word to transmit in a single register +- **input1** (64-bit mode): data in two consecutive registers — lower 32 bits in the allocated register, upper bits in the next register + +### Configuration +All TX parameters (channel, baud rate, bit order, start/stop polarity) are automatically +read from the paired \`UART Config\` block. Only \`Data Bits\` is set on this block directly, +since it determines the input port type (32-bit vs 64-bit) which the register allocator +needs to know at design time. + +### Data Bits +- Number of payload bits to transmit (excluding start and stop bits) +- Range: 1–62 +- dataBits ≤ 29: single-shot mode, 1–4 FIFO bytes +- dataBits 30–32: specific-bit mode, 4–5 FIFO bytes +- dataBits 33–62: continuous mode, 5+ FIFO bytes with polling + +### Calculation Example +- UART Config: 192 MHz clock, 12 MHz baud, LSB first, start=1, stop=0 +- Data Bits: 16 → frame = start(1) + 16 data bits + stop(0) = 18 bits → 3 FIFO bytes +- Frame size written to CH_CFG0 by config block: 18 (dataBits + 2) + +### Terminology +- **Per-command Reinit**: Reinit triggered before every TX to clear FIFO and reset state machines +- **FIFO**: 32-bit TX FIFO in the ENDAT peripheral — data bytes pushed via R30[7:0] +- **tx_channel_go**: R31 bit 18 — triggers transmission of the loaded FIFO content +- **Busy bit**: R31[5/13/21] — 1 = last bit still on wire, 0 = TX complete +- **Single-shot**: TX_FRAME_SIZE set in CH_CFG0 — hardware stops after exactly that many bits +- **Continuous mode**: TX_FRAME_SIZE = 0 — hardware transmits until FIFO is empty + +--- +`; +} + +function getAIContext() { + return getLongDescription() + ` +## How to Configure (For AI/Scripting) + +This section describes how to programmatically configure the UART TX Op block in a .syscfg file. + +**CRITICAL**: The UART TX Op block requires a paired \`UART Config\` block with TX enabled. +The op block reads all TX parameters (channel, baud rate, bit order, start/stop polarity) +from the config block automatically. Only \`Data Bits\` is set on the op block itself. + +### Step 1 — Add and configure a UART Config block (TX enabled) + +\`\`\`javascript +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +uart_config1.$name = "PRU_UART_CONFIG_0"; +uart_config1.enableTX = true; +uart_config1.enableRX = false; // TX-only or TX+RX — set as needed +uart_config1.txChannel = 0; // Channel 0 (GPO0=CLK, GPO1=DOUT, GPO2=OE) +uart_config1.txClockSource = 0; // 192 MHz (UART_CLK) +uart_config1.txBaudRate = 12; // 12 MHz +uart_config1.txStartBitPolarity = 1; // Start bit = 1 (high) +uart_config1.txStopBitPolarity = 0; // Stop bit = 0 (low) +uart_config1.txBitSwap = true; // LSB first (standard UART) +\`\`\` + +### Step 2 — Add and configure the UART TX Op block + +\`\`\`javascript +const uart_tx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx_op", {}, false); +const uart_tx_op1 = uart_tx_op.addInstance(); +uart_tx_op1.$name = "PRU_UART_TX_OP_0"; +uart_tx_op1.dataBits = 16; // 16 data payload bits +uart_tx_op1.uartConfig = uart_config1; // Link to config block +\`\`\` + +### Step 3 — Connect control flow and data + +\`\`\`javascript +// Control flow: config must run before op +scripting.connect(uart_config1, "next", uart_tx_op1, "prev"); + +// Data: connect upstream data source to op input +scripting.connect(data_source_block, "output1", uart_tx_op1, "input1"); + +// Control flow out of op +scripting.connect(uart_tx_op1, "next", next_block, "prev"); +\`\`\` + +### Configuration Parameters + +| Parameter | Type | Valid Values | Default | Description | +|-----------|------|--------------|---------|-------------| +| dataBits | Integer | 1–62 | 8 | Number of data payload bits. Determines input port type (32-bit vs 64-bit) | + +All other TX parameters (channel, baud rate, bit order, start/stop polarity) are +read from the paired \`UART Config\` block — do not duplicate them here. + +### Input Port Type by Data Bits + +| dataBits | Input Port | Notes | +|----------|------------|-------| +| 1–32 | 32-bit (input32) | Single register input | +| 33–62 | 64-bit (input64) | Two consecutive registers; upstream block must produce 64-bit output | + +### Common Data Bit Configurations + +| Protocol | Data Bits | Frame Bits (dataBits+2) | Mode | +|----------|-----------|------------------------|------| +| Standard UART 8-bit | 8 | 10 | Single-shot | +| 16-bit payload | 16 | 18 | Single-shot | +| 24-bit payload | 24 | 26 | Single-shot | +| 29-bit payload | 29 | 31 | Single-shot (max single-shot) | +| 30-bit payload | 30 | 32 | Specific-bit mode | +| 32-bit payload | 32 | 34 | Specific-bit mode | +| 33-bit payload | 33 | 35 | Continuous mode | + +### Important Notes + +1. **UART Config must appear before UART TX Op** in the control flow (connect config "next" to op "prev"). + +2. **dataBits > 32 activates continuous mode** — input port becomes 64-bit. The upstream data source block must produce a 64-bit output. + +3. **No peripheral register writes in this block** — only per-command reinit, frame construction, FIFO load, and TX trigger. All register setup is in \`UART Config\`. + +4. **uartConfig linkage is mandatory** — always set \`uart_tx_op1.uartConfig = uart_config1\` or the block will error during validation. + +5. **All UART TX Op instances must use the same dataBits value** — \`UART Config\` uses the first instance's dataBits for TX_FRAME_SIZE configuration. Mismatched instances will generate incorrect assembly. +`; +} + +exports = { + displayName: "PRU UART TX Op", + defaultInstanceName: "PRU_UART_TX_OP_", + longDescription: getLongDescription(), + getAIContext: getAIContext, + uiView: "graph", + templates: { + "/pru_blocks/common/pru_syscfg.asm.xdt": null + }, + config: [ + { + name: "$shape", + hidden: true, + default: "square", + }, + { + name: "$topLabel", + hidden: true, + default: "", + }, + // ========== Read-only display fields from config block ========== + { + name: "configInfo", + displayName: "Config Block Settings", + description: "Parameters read from the UART TX Config block", + default: "No config block found", + readOnly: true, + getValue: (inst) => { + const cfg = getConfigInst(); + if (!cfg) return "No UART Config block found"; + if (!cfg.enableTX) return "UART Config block has TX disabled"; + return `Ch${cfg.txChannel}, ${inst.dataBits}b, ${cfg.txBitSwap ? "LSB" : "MSB"}, start=${cfg.txStartBitPolarity}, stop=${cfg.txStopBitPolarity}, ${cfg.txBaudRate}MHz`; + } + }, + // ========== User configurables needed by ports() — cannot use system.modules here ========== + // dataBits and channel must match the UART TX Config block settings + { + name: "dataBits", + displayName: "Data Bits", + description: "Must match the UART TX Config block. Determines number of input ports.", + default: 8, + range: [1, 62] + }, + // ========== Hidden fields used by code generator ========== + { + name: "constant1", + hidden: true, + default: "0, 8", + getValue: (inst) => { + const cfg = getConfigInst(); + const channel = cfg ? cfg.txChannel : 0; + const dataBits = inst.dataBits; + if (dataBits >= 30 && dataBits <= 32) { + return `${channel}`; + } + return `${channel}, ${dataBits}`; + } + }, + { + name: "opCode", + displayName: "m_uart_tx_op", + default: "m_uart_tx_op", + hidden: true, + getValue: (inst) => { + const cfg = getConfigInst(); + const pruNum = cfg ? cfg.pruSelect : 0; + const bitOrder = cfg ? (cfg.txBitSwap ? "lsb" : "msb") : "lsb"; + const startBit = cfg ? cfg.txStartBitPolarity : 1; + const stopBit = cfg ? cfg.txStopBitPolarity : 0; + const dataBits = inst.dataBits; + const needsStopSuffix = (startBit === stopBit); + const stopSuffix = needsStopSuffix ? `_stop${stopBit}` : ""; + + if (dataBits >= 30 && dataBits <= 32) { + return `m_uart_tx_op_${dataBits}bit_pru${pruNum}_${bitOrder}_start${startBit}${stopSuffix}`; + } else { + const frameSize = dataBits + 2; + const numFifoBytes = Math.floor((frameSize + 7) / 8); + return `m_uart_tx_op_${numFifoBytes}byte_pru${pruNum}_${bitOrder}_start${startBit}${stopSuffix}`; + } + } + }, + { + name: "outputReg", + default: "None", + hidden: true + }, + { + name: "numOfInputPorts", + default: 1, + hidden: true, + getValue: (inst) => 1 + }, + { + name: "numOfOutputPorts", + default: 0, + hidden: true + }, + { + name: "numOfConstants", + default: 1, + hidden: true + }, + { + name: "input1Size", + hidden: true, + default: 4, + getValue: (inst) => { + if (inst.dataBits > 32) return 8; + if (inst.dataBits <= 8) return 1; + if (inst.dataBits <= 16) return 2; + return 4; + } + }, + { + name: "noOfCycles", + displayName: "Number Of Cycles", + getValue: (inst) => { + const frameSize = inst.dataBits + 2; + const numFifoBytes = Math.floor((frameSize + 7) / 8); + if (numFifoBytes <= 4) { + return 6 + numFifoBytes; + } else { + const continuousBytes = numFifoBytes - 4; + return 6 + 4 + (continuousBytes * 6) + 5; + } + }, + default: 9 + }, + ], + ports: (inst) => { + const dataBits = inst.dataBits; + const inputType = dataBits > 32 ? "input64" : "input32"; + return [ + { name: "input1", type: inputType }, + { name: "prev", type: "PREV" }, + { name: "next", type: "NEXT" }, + ]; + }, + validate, + sharedModuleInstances: (_inst) => { + return [ + { + name: "uartConfig", + displayName: "UART Config", + moduleName: "/pru_blocks/pru_io_blocks/uart_config", + collapsed: false, + } + ]; + }, + moduleStatic: { + modules: function(inst) { + return [{ + name: "pru_register_allocator_validator", + moduleName: "/pru_blocks/common/pru_blocks_static_module" + }] + }, + }, + getMacro +} diff --git a/.metadata/sysconfig/.meta/soc/pru_blocks_am243x.syscfg.js b/.metadata/sysconfig/.meta/soc/pru_blocks_am243x.syscfg.js index 33dc7f8..0ad114e 100644 --- a/.metadata/sysconfig/.meta/soc/pru_blocks_am243x.syscfg.js +++ b/.metadata/sysconfig/.meta/soc/pru_blocks_am243x.syscfg.js @@ -24,6 +24,7 @@ const topModules_pru = [ "/pru_blocks/utils/label", "/pru_blocks/utils/look_up_table", "/pru_blocks/utils/access_look_up_table", + "/pru_blocks/utils/data_splitter", ], categories: [ { @@ -68,8 +69,9 @@ const topModules_pru = [ { displayName: "SERIAL PROTOCOL EMULATION", modules: [ - "/pru_blocks/pru_io_blocks/uart_tx", - "/pru_blocks/pru_io_blocks/uart_rx", + "/pru_blocks/pru_io_blocks/uart_rx_op", + "/pru_blocks/pru_io_blocks/uart_tx_op", + "/pru_blocks/pru_io_blocks/uart_config", "/pru_blocks/pru_io_blocks/pru_spi_write", "/pru_blocks/pru_io_blocks/pru_spi_read", "/pru_blocks/pru_io_blocks/pru_spi_transfer", diff --git a/.metadata/sysconfig/.meta/soc/pru_blocks_am261x.syscfg.js b/.metadata/sysconfig/.meta/soc/pru_blocks_am261x.syscfg.js index 33dc7f8..0ad114e 100644 --- a/.metadata/sysconfig/.meta/soc/pru_blocks_am261x.syscfg.js +++ b/.metadata/sysconfig/.meta/soc/pru_blocks_am261x.syscfg.js @@ -24,6 +24,7 @@ const topModules_pru = [ "/pru_blocks/utils/label", "/pru_blocks/utils/look_up_table", "/pru_blocks/utils/access_look_up_table", + "/pru_blocks/utils/data_splitter", ], categories: [ { @@ -68,8 +69,9 @@ const topModules_pru = [ { displayName: "SERIAL PROTOCOL EMULATION", modules: [ - "/pru_blocks/pru_io_blocks/uart_tx", - "/pru_blocks/pru_io_blocks/uart_rx", + "/pru_blocks/pru_io_blocks/uart_rx_op", + "/pru_blocks/pru_io_blocks/uart_tx_op", + "/pru_blocks/pru_io_blocks/uart_config", "/pru_blocks/pru_io_blocks/pru_spi_write", "/pru_blocks/pru_io_blocks/pru_spi_read", "/pru_blocks/pru_io_blocks/pru_spi_transfer", diff --git a/.metadata/sysconfig/.meta/soc/pru_blocks_am64x.syscfg.js b/.metadata/sysconfig/.meta/soc/pru_blocks_am64x.syscfg.js index 33dc7f8..0ad114e 100644 --- a/.metadata/sysconfig/.meta/soc/pru_blocks_am64x.syscfg.js +++ b/.metadata/sysconfig/.meta/soc/pru_blocks_am64x.syscfg.js @@ -24,6 +24,7 @@ const topModules_pru = [ "/pru_blocks/utils/label", "/pru_blocks/utils/look_up_table", "/pru_blocks/utils/access_look_up_table", + "/pru_blocks/utils/data_splitter", ], categories: [ { @@ -68,8 +69,9 @@ const topModules_pru = [ { displayName: "SERIAL PROTOCOL EMULATION", modules: [ - "/pru_blocks/pru_io_blocks/uart_tx", - "/pru_blocks/pru_io_blocks/uart_rx", + "/pru_blocks/pru_io_blocks/uart_rx_op", + "/pru_blocks/pru_io_blocks/uart_tx_op", + "/pru_blocks/pru_io_blocks/uart_config", "/pru_blocks/pru_io_blocks/pru_spi_write", "/pru_blocks/pru_io_blocks/pru_spi_read", "/pru_blocks/pru_io_blocks/pru_spi_transfer", From 592d258f17a86d049db506b5d344cb0b9bc8fd84 Mon Sep 17 00:00:00 2001 From: Ayushman Date: Mon, 8 Jun 2026 16:26:57 +0530 Subject: [PATCH 2/5] am243x/am261x/am64x: add 64 bit port support - add 64 bit port support - add data splitter block Signed-off-by: Ayushman --- .../sysconfig/.meta/pru_blocks.syscfg.js | 34 + .../application_specific/crc_block.syscfg.js | 6 +- .../common/pru_pin_usage_summary.syscfg.xdt | 35 +- .../pru_register_allocator.js | 24 +- .../data_handling/arithmetic_block.syscfg.js | 4 +- .../data_handling/bitwise_block.syscfg.js | 4 +- .../load_constant_block.syscfg.js | 4 +- .../conditional_block.syscfg.js | 5 +- .../pru_io_blocks/pru_gpi_block.syscfg.js | 4 +- .../pru_io_blocks/pru_gpo_block.syscfg.js | 4 +- .../pru_io_blocks/pru_spi_read.syscfg.js | 4 +- .../pru_io_blocks/pru_spi_transfer.syscfg.js | 4 +- .../pru_io_blocks/pru_spi_write.syscfg.js | 4 +- .../pru_io_blocks/uart_rx.syscfg.js | 1107 ----------- .../pru_io_blocks/uart_tx.syscfg.js | 1657 ----------------- .../utils/access_look_up_table.syscfg.js | 4 +- .../pru_blocks/utils/data_splitter.syscfg.js | 148 ++ .../pru_blocks/utils/delay_block.syscfg.js | 4 +- .../pru_blocks/utils/look_up_table.syscfg.js | 5 +- .../utils/memory_access_block.syscfg.js | 67 +- 20 files changed, 298 insertions(+), 2830 deletions(-) delete mode 100644 .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx.syscfg.js delete mode 100644 .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx.syscfg.js create mode 100644 .metadata/sysconfig/.meta/pru_blocks/utils/data_splitter.syscfg.js diff --git a/.metadata/sysconfig/.meta/pru_blocks.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks.syscfg.js index cc51d3b..8f7b34e 100644 --- a/.metadata/sysconfig/.meta/pru_blocks.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks.syscfg.js @@ -39,6 +39,27 @@ exports = { side: "right", multipleConnections: true, }, + // Typed data ports — width-enforced connections + { + type: "input32", + side: "left", + multipleConnections: false, + }, + { + type: "output32", + side: "right", + multipleConnections: true, + }, + { + type: "input64", + side: "left", + multipleConnections: false, + }, + { + type: "output64", + side: "right", + multipleConnections: true, + }, { type: "CONDITIONAL_NEXT", side: "right", @@ -77,6 +98,19 @@ exports = { type: "data", displayName: "dataflow", }, + // Typed width-enforced data connections + { + start: "output32", + end: "input32", + type: "data", + displayName: "dataflow32", + }, + { + start: "output64", + end: "input64", + type: "data", + displayName: "dataflow64", + }, { start: "CONDITIONAL_NEXT", end: "PREV", diff --git a/.metadata/sysconfig/.meta/pru_blocks/application_specific/crc_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/application_specific/crc_block.syscfg.js index dfb50bd..de0dd4b 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/application_specific/crc_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/application_specific/crc_block.syscfg.js @@ -603,14 +603,14 @@ exports = { ports: (inst) => { let ports = []; - ports.push({ name: "input1", displayName:"init", type: "input" }) - ports.push({ name: "input2", displayName:"data", type: "input" }) + ports.push({ name: "input1", displayName:"init", type: "input32" }) + ports.push({ name: "input2", displayName:"data", type: "input32" }) ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } return ports diff --git a/.metadata/sysconfig/.meta/pru_blocks/common/pru_pin_usage_summary.syscfg.xdt b/.metadata/sysconfig/.meta/pru_blocks/common/pru_pin_usage_summary.syscfg.xdt index 2b2cd0a..62652e1 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/common/pru_pin_usage_summary.syscfg.xdt +++ b/.metadata/sysconfig/.meta/pru_blocks/common/pru_pin_usage_summary.syscfg.xdt @@ -64,26 +64,23 @@ % pins.push({ pin: inst["SDO Signal"], func: "SDO", dir: `${PRU_USED}_GPO` }); % pins.push({ pin: inst["CS Signal"], func: "CS", dir: isCtrl ? `${PRU_USED}_GPO` : `${PRU_USED}_GPI` }); % } -% // UART TX block - uses ENDAT peripheral with fixed pins per channel -% else if (modulePath.includes("uart_tx")) { -% const ch = inst.channel; -% // Channel 0: GPO0 (clock), GPO1 (data out), GPO2 (out enable) -% // Channel 1: GPO3, GPO4, GPO5 -% // Channel 2: GPO6, GPO7, GPO8 -% const basePin = ch * 3; -% pins.push({ pin: basePin, func: "PERIF" + ch + "_CLK", dir: `${PRU_USED}_GPO` }); -% pins.push({ pin: basePin + 1, func: "PERIF" + ch + "_DOUT", dir: `${PRU_USED}_GPO` }); -% pins.push({ pin: basePin + 2, func: "PERIF" + ch + "_OE", dir: `${PRU_USED}_GPO` }); -% } -% // UART RX block - uses ENDAT peripheral with fixed pins per channel -% else if (modulePath.includes("uart_rx")) { -% const ch = inst.channel; -% // Channel 0: GPI9 (perif_data_in) -% // Channel 1: GPI10 -% // Channel 2: GPI11 -% const rxPin = 9 + ch; -% pins.push({ pin: rxPin, func: "PERIF" + ch + "_DIN", dir: `${PRU_USED}_GPI` }); +% // UART Config block - covers pins for all enabled TX/RX channels +% // TX op and RX op blocks share the same pins — only uart_config is the source of truth +% else if (modulePath.includes("uart_config")) { +% if (inst.enableTX) { +% const ch = inst.txChannel; +% const basePin = ch * 3; +% pins.push({ pin: basePin, func: "PERIF" + ch + "_CLK", dir: `${PRU_USED}_GPO` }); +% pins.push({ pin: basePin + 1, func: "PERIF" + ch + "_DOUT", dir: `${PRU_USED}_GPO` }); +% pins.push({ pin: basePin + 2, func: "PERIF" + ch + "_OE", dir: `${PRU_USED}_GPO` }); +% } +% if (inst.enableRX) { +% const ch = inst.rxChannel; +% const rxPin = 9 + ch; +% pins.push({ pin: rxPin, func: "PERIF" + ch + "_DIN", dir: `${PRU_USED}_GPI` }); +% } % } +% // UART TX Op / UART RX Op — pins already covered by the paired uart_config instance above % return pins; %} % diff --git a/.metadata/sysconfig/.meta/pru_blocks/common/register_allocation/pru_register_allocator.js b/.metadata/sysconfig/.meta/pru_blocks/common/register_allocation/pru_register_allocator.js index fd57fb2..d224604 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/common/register_allocation/pru_register_allocator.js +++ b/.metadata/sysconfig/.meta/pru_blocks/common/register_allocation/pru_register_allocator.js @@ -549,10 +549,16 @@ function pushInstruction(instance, parentInstance) { maxBytesUsed = noOfBytesUsedByInput > maxBytesUsed ? noOfBytesUsedByInput : maxBytesUsed; // Get register notation for this input's output - inputValues = inputValues + getPruRegister( - moduleInstanceRegisterMap[inputInstanceName].byteOffsetOfOutput1, - moduleInstanceRegisterMap[inputInstanceName].numOfBytesReqByOutput1 - ) + " , "; + const _srcByteOffset = moduleInstanceRegisterMap[inputInstanceName].byteOffsetOfOutput1; + const _srcNumBytes = moduleInstanceRegisterMap[inputInstanceName].numOfBytesReqByOutput1; + if (_srcNumBytes > 4) { + // 64-bit source: emit two consecutive registers (lo, hi) + const _loReg = getPruRegister(_srcByteOffset, 4); + const _hiReg = getPruRegister(_srcByteOffset + 4, 4); + inputValues = inputValues + _loReg + " , " + _hiReg + " , "; + } else { + inputValues = inputValues + getPruRegister(_srcByteOffset, _srcNumBytes) + " , "; + } } } @@ -728,7 +734,15 @@ function pushInstruction(instance, parentInstance) { moduleInstanceRegisterMap[instanceName].numOfBytesReqByOutput1 = maxBytesUsed; // Get output register notation - const outputRegister = getPruRegister(byteOffset, maxBytesUsed); + // For 64-bit outputs (>4 bytes), emit two consecutive registers (lo, hi) + let outputRegister; + if (maxBytesUsed > 4) { + const _outLoReg = getPruRegister(byteOffset, 4); + const _outHiReg = getPruRegister(byteOffset + 4, 4); + outputRegister = `${_outLoReg}, ${_outHiReg}`; + } else { + outputRegister = getPruRegister(byteOffset, maxBytesUsed); + } // Clean up input values string (remove trailing comma and space) if (inputValues.endsWith(" , ")) { diff --git a/.metadata/sysconfig/.meta/pru_blocks/data_handling/arithmetic_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/data_handling/arithmetic_block.syscfg.js index 9006114..bbcbf94 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/data_handling/arithmetic_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/data_handling/arithmetic_block.syscfg.js @@ -250,11 +250,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/data_handling/bitwise_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/data_handling/bitwise_block.syscfg.js index 01246ac..dede553 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/data_handling/bitwise_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/data_handling/bitwise_block.syscfg.js @@ -307,11 +307,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/data_handling/load_constant_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/data_handling/load_constant_block.syscfg.js index 6c1b3cc..bae4c7c 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/data_handling/load_constant_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/data_handling/load_constant_block.syscfg.js @@ -252,11 +252,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/program_control/conditional_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/program_control/conditional_block.syscfg.js index ff0c1f6..e93f8ae 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/program_control/conditional_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/program_control/conditional_block.syscfg.js @@ -98,6 +98,8 @@ scripting.connect(prev_block, "next", if_else1, "prev"); 4. **Single Cycle**: Comparison and branch decision execute in 1 PRU cycle. 5. **Port Names**: True path uses "T" port, False path uses "F" port (displayed as t_next/f_next). + +6. **Terminate Each Branch with a Flow Control Block**: The code generator places the FALSE path immediately after the branch instruction, with the TRUE path at the branch target label. If the FALSE path has no explicit terminator, execution falls through into the TRUE path — causing both branches to execute regardless of the condition. Always end each branch (T and F) with a Flow Control block (HALT or END) to prevent this fall-through. `; } @@ -168,6 +170,7 @@ Implements conditional logic (IF/ELSE statements) to control program flow based - The conditional check happens instantly (1 cycle) - Code on both branches is generated, only one path executes at runtime - This block does not produce an output value - it only controls flow +- **Always terminate each branch (T and F) with a Flow Control block**: The FALSE path falls through to the TRUE path in the generated assembly unless explicitly stopped. Without a terminator on the FALSE branch, both branches execute sequentially regardless of the condition result. ### Terminology - **Conditional branching**: Changing program flow based on a condition @@ -292,7 +295,7 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } ports.push({ name: "T",displayName: "t_next", type: "CONDITIONAL_NEXT"}) ports.push({ name: "F",displayName: "f_next", type: "CONDITIONAL_NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpi_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpi_block.syscfg.js index 4de0444..581800c 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpi_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpi_block.syscfg.js @@ -308,11 +308,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "IO_PREV" }) ports.push({ name: "next", type: "IO_NEXT" }) diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpo_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpo_block.syscfg.js index 3cfc607..6ce7c6a 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpo_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_gpo_block.syscfg.js @@ -352,11 +352,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "IO_PREV" }) ports.push({ name: "next", type: "IO_NEXT" }) diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_read.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_read.syscfg.js index 9b3c94f..69f6cbb 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_read.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_read.syscfg.js @@ -1393,11 +1393,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_transfer.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_transfer.syscfg.js index 71ed1ce..59fd443 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_transfer.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_transfer.syscfg.js @@ -1653,11 +1653,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_write.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_write.syscfg.js index 1cea1ea..be03889 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_write.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spi_write.syscfg.js @@ -1421,11 +1421,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx.syscfg.js deleted file mode 100644 index 4c0faad..0000000 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_rx.syscfg.js +++ /dev/null @@ -1,1107 +0,0 @@ -/** - * Helper function to extract PRU number from system context - * @returns {number} PRU number (0 or 1), defaults to 0 if cannot determine - */ -function getPruNumberFromContext() { - const common = system.getScript("/common"); - const coreName = common.getSelfSysCfgCoreName(); - - // coreName format: "icss_g0_pru0" or "icss_g0_pru1" - if (coreName && coreName.includes("pru")) { - const match = coreName.match(/pru(\d+)$/); - if (match && match[1]) { - return parseInt(match[1]); - } - } - return 0; -} -const PRU_USED = getPruNumberFromContext(); -function validate(inst, report) { - // Get oversample multiplier from encoding (0=1x, 1=2x, 3=4x, 7=8x) - let oversampleMultiplier; - if (inst.oversampleSize === 0) { - oversampleMultiplier = 1; - } else if (inst.oversampleSize === 1) { - oversampleMultiplier = 2; - } else if (inst.oversampleSize === 3) { - oversampleMultiplier = 4; - } else { - oversampleMultiplier = 8; - } - - // Get clock frequency based on clock source selection - // 0 = 192 MHz (ICSSGn_UART_CLK), 1 = 200 MHz (ICSSGn_CORE_CLK) - const uartClkMHz = inst.clockSource === 0 ? 192 : 200; - const baudRateTimesOversample = inst.baudRate * oversampleMultiplier; - - // Check divisibility: clock % (baudRate * oversample) should be 0 - if (uartClkMHz % baudRateTimesOversample !== 0) { - report.logError(`Clock (${uartClkMHz} MHz) is not divisible by (baudRate * oversample) = ${inst.baudRate} * ${oversampleMultiplier} = ${baudRateTimesOversample}. Choose a baud rate where ${uartClkMHz} is divisible by (baudRate * oversample).`, inst, "baudRate"); - } else { - // Check clock divider range - const clockDivider = (uartClkMHz / baudRateTimesOversample) - 1; - if (clockDivider < 0 || clockDivider > 65535) { - report.logError(`Calculated clock divider (${clockDivider}) is out of range (0-65535). Adjust baud rate.`, inst, "baudRate"); - } - } - - // Validate frame size - currently limited to 32 bits (30 data bits + start + stop) - // Extended mode (>32 bits) requires multi-register handling that is not yet implemented - if (inst.frameSize < 3 || inst.frameSize > 32) { - report.logError("Frame size must be between 3 and 32 bits (1-30 data bits + start + stop). Extended mode (>32 bits) is not currently supported.", inst, "frameSize"); - } -} - -/** - * Returns guarded .set/.asg definitions for ICSS_CFG register symbols. - * Emitted once at the top of the generated macro so the assembler has the - * symbols available even when icss_cfg_regs.inc / icss_regs.inc are not - * explicitly included by the user. - */ -function getIcssCfgDefinitions() { - return `\ -; ========== ICSS CFG register definitions (auto-generated, guarded) ========== - .if !$isdefed("ICSS_CFG") - .asg c4, ICSS_CFG - .endif - .if !$isdefed("ICSS_CFG_GPCFG0") -ICSS_CFG_GPCFG0 .set 0x0008 - .endif - .if !$isdefed("ICSS_CFG_GPCFG1") -ICSS_CFG_GPCFG1 .set 0x000C - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_RXCFG") -ICSS_CFG_PRU0_ENDAT_RXCFG .set 0x00E0 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_RXCFG") -ICSS_CFG_PRU1_ENDAT_RXCFG .set 0x0100 - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH0_CFG0") -ICSS_CFG_PRU0_ENDAT_CH0_CFG0 .set 0x00E8 - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH1_CFG0") -ICSS_CFG_PRU0_ENDAT_CH1_CFG0 .set 0x00F0 - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH2_CFG0") -ICSS_CFG_PRU0_ENDAT_CH2_CFG0 .set 0x00F8 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH0_CFG0") -ICSS_CFG_PRU1_ENDAT_CH0_CFG0 .set 0x0108 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH1_CFG0") -ICSS_CFG_PRU1_ENDAT_CH1_CFG0 .set 0x0110 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH2_CFG0") -ICSS_CFG_PRU1_ENDAT_CH2_CFG0 .set 0x0118 - .endif -; ============================================================================ -`; -} - -/** - * Returns the body of the UART RX configuration macro - * @param {string} pruInstructionMacro - The macro instruction string - * @param {string} opCode - The operation code (macro name) - * @returns {string} The full macro body as a string with parameters replaced - */ -function getMacro(pruInstructionMacro, opCode) { - // When generating the macro definition (not an inline call), prepend the - // guarded ICSS_CFG register definitions so they are available to the assembler. - const prefix = (pruInstructionMacro === "") ? getIcssCfgDefinitions() : ""; - - let macroBody = ""; - - // Check if this is extended mode (>32 data bits, needs two output registers) - const isExtended = opCode.includes("_ext"); - - // Extract parameters from the pruInstructionMacro - // Parameter order follows system convention: , - // Extended mode (8 bytes): "R0, R1, channel, ..." - two output registers - // Standard mode (4 bytes): "R0, channel, ..." - one output register - const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); - const macroName = parts[0] || opCode; - - // Determine if this is extended mode based on macro name - let dataByteRegLo, dataByteRegHi, channel, clockDiv, rxClkSel, oversample, startBitPol, frameSize, bitSwap; - - if (isExtended) { - // Extended mode: two output registers (8 bytes = "R0, R1") - dataByteRegLo = parts[1] || "dataByteRegLo"; // Output register for lower 32 bits - dataByteRegHi = parts[2] || "dataByteRegHi"; // Output register for upper 32 bits - channel = parts[3] || "channel"; - clockDiv = parts[4] || "clockDiv"; - rxClkSel = parts[5] || "rxClkSel"; // RX clock source: 0=192MHz, 1=200MHz - oversample = parts[6] || "oversample"; - startBitPol = parts[7] || "startBitPol"; - frameSize = parts[8] || "frameSize"; - bitSwap = parts[9] || "bitSwap"; - } else { - // Standard mode: one output register (4 bytes = "R0") - dataByteRegLo = parts[1] || "dataByteRegLo"; // Output register for data (up to 32 bits) - dataByteRegHi = "0"; // Not used in standard mode, set to 0 - channel = parts[2] || "channel"; - clockDiv = parts[3] || "clockDiv"; - rxClkSel = parts[4] || "rxClkSel"; // RX clock source: 0=192MHz, 1=200MHz - oversample = parts[5] || "oversample"; - startBitPol = parts[6] || "startBitPol"; - frameSize = parts[7] || "frameSize"; - bitSwap = parts[8] || "bitSwap"; - } - - // Internal registers using TEMP_REG1/TEMP_REG2 for temp operations - // For standard mode: TEMP_REG1 = bit accumulation, TEMP_REG2.b0 = counter, TEMP_REG2.b1 = temp sample - // Output: dataByteRegLo = extracted data, dataByteRegHi = 0 - // For extended mode: output registers (dataByteRegLo/dataByteRegHi) used for 64-bit accumulation - // TEMP_REG1.b0 = counter, TEMP_REG1.b1 = temp sample, TEMP_REG2 = temp calcs - const bitAccumReg = "TEMP_REG1"; // For standard mode accumulation - const bitCounterReg = isExtended ? "TEMP_REG1.b0" : "TEMP_REG2.b0"; - const tempSampleReg = isExtended ? "TEMP_REG1.b1" : "TEMP_REG2.b1"; - - if (pruInstructionMacro == "") { - if (isExtended) { - // Extended mode: two output registers in macro signature - macroBody = macroName + " " + ".macro " + dataByteRegLo + ", " + dataByteRegHi + ", " + channel + ", " + clockDiv + ", " + - rxClkSel + ", " + oversample + ", " + startBitPol + ", " + frameSize + ", " + bitSwap + "\n"; - macroBody += ` ; Parameters (Extended Mode - >=31 data bits): - ; dataByteRegLo - Output register for lower 32 bits of extracted data (dynamically allocated) - ; dataByteRegHi - Output register for upper bits of extracted data (dynamically allocated) - ; channel - ENDAT channel (0, 1, or 2) - ; clockDiv - RX clock divider (1-65535): RX_freq = core_clk / (clockDiv + 1) - ; rxClkSel - RX clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) - ; oversample - Oversample size encoding (0=1x, 1=2x, 3=4x, 7=8x) - bits[2:0] value - ; startBitPol - Start bit polarity (0=falling, 1=rising edge) - ; frameSize - Frame size in bits (33-64 for extended mode) - ; bitSwap - Bit swap/LSB first (0=disabled, 1=enabled) - ; Internal registers (TEMP_REG1/TEMP_REG2 are used for counters and temp calculations) -`; - } else { - // Standard mode: one output register in macro signature - macroBody = macroName + " " + ".macro " + dataByteRegLo + ", " + channel + ", " + clockDiv + ", " + - rxClkSel + ", " + oversample + ", " + startBitPol + ", " + frameSize + ", " + bitSwap + "\n"; - macroBody += ` ; Parameters (Standard Mode - <=30 data bits): - ; dataByteRegLo - Output register for extracted data (dynamically allocated) - ; channel - ENDAT channel (0, 1, or 2) - ; clockDiv - RX clock divider (1-65535): RX_freq = core_clk / (clockDiv + 1) - ; rxClkSel - RX clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) - ; oversample - Oversample size encoding (0=1x, 1=2x, 3=4x, 7=8x) - bits[2:0] value - ; startBitPol - Start bit polarity (0=falling, 1=rising edge) - ; frameSize - Frame size in bits (3-32 for standard mode, typical 10 for UART) - ; bitSwap - Bit swap/LSB first (0=disabled, 1=enabled) - ; Internal registers (TEMP_REG1/TEMP_REG2 are used for bit accumulation and counters) -`; - } - } - - // Check if this is one of our UART RX config macros - // macroName format: m_uart_rx_config_pru0_lsb_8x or m_uart_rx_config_pru1_msb_4x - const isPRU0 = macroName.includes("_pru0_"); - const isPRU1 = macroName.includes("_pru1_"); - const isLSB = macroName.includes("_lsb_"); - const isMSB = macroName.includes("_msb_"); - - // Determine PRU-specific register names - const pruNum = isPRU0 ? "0" : "1"; - const gpcfgReg = isPRU0 ? "ICSS_CFG_GPCFG0" : "ICSS_CFG_GPCFG1"; - const rxcfgReg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_RXCFG" : "ICSS_CFG_PRU1_ENDAT_RXCFG"; - const ch0cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH0_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH0_CFG0"; - const ch1cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH1_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH1_CFG0"; - const ch2cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH2_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH2_CFG0"; - - if (isLSB || isMSB) { - macroBody += ` - ; ========== Global Reinit THEN de-assert rx_en (TRM Table 6-424 sequence) ========== - ; TRM note: "assert tx_global_reinit then de-assert rx_en" - set r31, r31, 19 ; 1. Trigger reinit first - ; ========== Wait for Reinit Complete ========== - ; Poll busy bit for selected channel (1=active, 0=done) - .if ${channel} == 0 -wait_reinit?: - qbbs wait_reinit?, r31, 5 - .elseif ${channel} == 1 -wait_reinit?: - qbbs wait_reinit?, r31, 13 - .else -wait_reinit?: - qbbs wait_reinit?, r31, 21 - .endif - ldi r30.b3, 0x00 ; 2. De-assert rx_en after reinit has settled - - ; ========== Configure GPCFG${pruNum} for PRU${pruNum} Peripheral Mode ========== - ; Set mux mode to peripheral mode for PRU${pruNum} - ldi32 TEMP_REG1, 0x04000000 - ldi TEMP_REG2.w0, ${gpcfgReg} - sbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - - ; ========== Configure RX Clock and Oversample ========== - ; Build RXCFG register value: - ; [31:16] = clockDiv (RX_DIV_FACTOR) - ; [4] = RX_CLK_SEL (0=192MHz, 1=200MHz) - ; [3] = startBitPol (RX_SB_POL) - ; [2:0] = oversample encoding (RX_OVERSAMPLE_SIZE) - ldi TEMP_REG1.w0, (${rxClkSel} << 4) | ${oversample} ; Clock select bit[4] + Oversample bits[2:0] -`; - // Only add startBitPol check if it's not 0 - if (startBitPol !== "0") { - macroBody += ` set TEMP_REG1.w0, TEMP_REG1.w0, 3 ; Set bit 3 (startBitPol=1)\n`; - } - macroBody += ` ldi TEMP_REG1.w2, ${clockDiv} ; Clock divider bits[31:16] - ldi TEMP_REG2.w0, ${rxcfgReg} - sbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - - ; ========== Configure Channel CFG0 Register ========== - ; Build CHx_CFG0 register value: - ; [31] = bitSwap (LSB first if set) - ; [27:16] = frameSize (number of bits to receive) - ldi TEMP_REG1.w0, 0 ; Clear lower word - ldi TEMP_REG1.w2, ${frameSize} ; Frame size in bits[27:16] (upper word) -`; - // Only set bit 31 for LSB macros - if (isLSB) { - macroBody += ` set TEMP_REG1, TEMP_REG1, 31 ; Set bit 31 (bitSwap=1, LSB first)\n`; - } - // For MSB macros, bit 31 stays 0 (MSB first) - - // Select CHx_CFG0 offset based on channel (at macro generation time) - let ch_cfg_offset = ""; - if (channel === "0") { - ch_cfg_offset = ch0cfg0Reg; - } else if (channel === "1") { - ch_cfg_offset = ch1cfg0Reg; - } else { - ch_cfg_offset = ch2cfg0Reg; - } - - macroBody += ` ldi TEMP_REG2.w0, ${ch_cfg_offset} - sbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - - ; ========== Enable RX for Selected Channel ========== -`; - // Select RX enable bit based on channel (at macro generation time) - let rx_enable_bit = ""; - if (channel === "0") { - rx_enable_bit = "24"; - } else if (channel === "1") { - rx_enable_bit = "25"; - } else { - rx_enable_bit = "26"; - } - - // Determine rx_data register byte index - let rx_data_byte = ""; - if (channel === "0") { - rx_data_byte = "0"; - } else if (channel === "1") { - rx_data_byte = "1"; - } else { - rx_data_byte = "2"; - } - - // Determine middle bit based on macro name - let middle_bit; - if (macroName.includes("_1x")) { - middle_bit = 0; - } else if (macroName.includes("_2x")) { - middle_bit = 0; - } else if (macroName.includes("_4x")) { - middle_bit = 2; - } else { - middle_bit = 4; - } - - // Calculate R30.b3 value with RX enable bit set (avoid read-modify-write on full R30) - // Bit 24 = CH0 (0x01), Bit 25 = CH1 (0x02), Bit 26 = CH2 (0x04) in byte 3 - let r30_b3_value; - if (channel === "0") { - r30_b3_value = "0x01"; // Bit 24 - } else if (channel === "1") { - r30_b3_value = "0x02"; // Bit 25 - } else { - r30_b3_value = "0x04"; // Bit 26 - } - macroBody += ` ldi r30.b3, ${r30_b3_value} - -`; - - // ========== Bit Accumulation Logic ========== - if (isExtended) { - // Extended mode: use two output registers for 64-bit accumulation - macroBody += ` ldi ${dataByteRegLo}, 0 - ldi ${dataByteRegHi}, 0 - ldi ${bitCounterReg}, 0 - -rx_bit_loop?: - qbbc rx_bit_loop?, r31, ${rx_enable_bit} - mov ${tempSampleReg}, r31.b${rx_data_byte} - set r31, r31, ${rx_enable_bit} - - qbbs set_bit?, ${tempSampleReg}, ${middle_bit} -`; - // Bit accumulation for extended mode based on LSB vs MSB - if (isLSB) { - // LSB first: bits shift right through Hi then Lo, new bit enters at Hi bit 31 - macroBody += ` ; Shift right through both registers (bit 0 of Hi goes to bit 31 of Lo) - lsr ${dataByteRegLo}, ${dataByteRegLo}, 1 - qbbc no_carry?, ${dataByteRegHi}, 0 - set ${dataByteRegLo}, ${dataByteRegLo}, 31 -no_carry?: - lsr ${dataByteRegHi}, ${dataByteRegHi}, 1 - qba next_bit? -set_bit?: - ; Shift right and set incoming bit at Hi bit 31 - lsr ${dataByteRegLo}, ${dataByteRegLo}, 1 - qbbc no_carry_set?, ${dataByteRegHi}, 0 - set ${dataByteRegLo}, ${dataByteRegLo}, 31 -no_carry_set?: - lsr ${dataByteRegHi}, ${dataByteRegHi}, 1 - set ${dataByteRegHi}, ${dataByteRegHi}, 31 -`; - } else { - // MSB first: bits shift left through Lo then Hi, new bit enters at Lo bit 0 - macroBody += ` ; Shift left through both registers (bit 31 of Lo goes to bit 0 of Hi) - lsl ${dataByteRegHi}, ${dataByteRegHi}, 1 - qbbc no_carry?, ${dataByteRegLo}, 31 - set ${dataByteRegHi}, ${dataByteRegHi}, 0 -no_carry?: - lsl ${dataByteRegLo}, ${dataByteRegLo}, 1 - qba next_bit? -set_bit?: - ; Shift left and set incoming bit at Lo bit 0 - lsl ${dataByteRegHi}, ${dataByteRegHi}, 1 - qbbc no_carry_set?, ${dataByteRegLo}, 31 - set ${dataByteRegHi}, ${dataByteRegHi}, 0 -no_carry_set?: - lsl ${dataByteRegLo}, ${dataByteRegLo}, 1 - set ${dataByteRegLo}, ${dataByteRegLo}, 0 -`; - } - macroBody += `next_bit?: - add ${bitCounterReg}, ${bitCounterReg}, 1 - qbgt rx_bit_loop?, ${bitCounterReg}, frameSize - -`; - } else { - // Standard mode: use single TEMP_REG1 for 32-bit accumulation - macroBody += ` ldi ${bitAccumReg}, 0 - ldi ${bitCounterReg}, 0 - -rx_bit_loop?: - qbbc rx_bit_loop?, r31, ${rx_enable_bit} - mov ${tempSampleReg}, r31.b${rx_data_byte} - set r31, r31, ${rx_enable_bit} - - qbbs set_bit?, ${tempSampleReg}, ${middle_bit} -`; - // Bit accumulation for standard mode based on LSB vs MSB - if (isLSB) { - macroBody += ` lsr ${bitAccumReg}, ${bitAccumReg}, 1 - qba next_bit? -set_bit?: - lsr ${bitAccumReg}, ${bitAccumReg}, 1 - set ${bitAccumReg}, ${bitAccumReg}, 31 -`; - } else { - macroBody += ` lsl ${bitAccumReg}, ${bitAccumReg}, 1 - qba next_bit? -set_bit?: - lsl ${bitAccumReg}, ${bitAccumReg}, 1 - set ${bitAccumReg}, ${bitAccumReg}, 0 -`; - } - macroBody += `next_bit?: - add ${bitCounterReg}, ${bitCounterReg}, 1 - qbgt rx_bit_loop?, ${bitCounterReg}, frameSize - -`; - } - - // ========== Extraction Logic ========== - // Data bits = frameSize - 2 (remove start and stop bits) - if (isExtended) { - // Extended mode extraction: data spans two registers (64-bit) - if (isLSB) { - // LSB first extended: bits accumulated at high end of Hi:Lo pair - // After receiving frameSize bits, they are in bits [63 : 64-frameSize] - // We need to shift right by (64 - frameSize) to align, then remove start bit - macroBody += ` ; Extract data (extended LSB): shift to align, remove start bit - ; Shift right by (64 - frameSize) across both registers - ldi TEMP_REG2.w0, 64 - sub TEMP_REG2.w0, TEMP_REG2.w0, ${frameSize} - ; Shift Hi:Lo right by TEMP_REG2.w0 bits -shift_loop?: - qbeq shift_done?, TEMP_REG2.w0, 0 - ; Shift right by 1: carry bit 0 of Hi to bit 31 of Lo - lsr ${dataByteRegLo}, ${dataByteRegLo}, 1 - qbbc no_shift_carry?, ${dataByteRegHi}, 0 - set ${dataByteRegLo}, ${dataByteRegLo}, 31 -no_shift_carry?: - lsr ${dataByteRegHi}, ${dataByteRegHi}, 1 - sub TEMP_REG2.w0, TEMP_REG2.w0, 1 - qba shift_loop? -shift_done?: - ; Now shift right by 1 more to remove start bit - lsr ${dataByteRegLo}, ${dataByteRegLo}, 1 - qbbc no_start_carry?, ${dataByteRegHi}, 0 - set ${dataByteRegLo}, ${dataByteRegLo}, 31 -no_start_carry?: - lsr ${dataByteRegHi}, ${dataByteRegHi}, 1 - ; Data bits are now aligned at Lo bit 0, spanning Lo and Hi - ; Mask to keep only data bits - ; For frameSize 33: dataBits=31, stop bit may be carried to Lo bit 31, clear it - ; For frameSize 34: dataBits=32, all data in Lo after shift, just clear Hi - ; For frameSize 35+: dataBits=33+, upper bits in Hi = frameSize - 34 - .if ${frameSize} == 33 - clr ${dataByteRegLo}, ${dataByteRegLo}, 31 ; Clear any garbage carried to Lo bit 31 - ldi ${dataByteRegHi}, 0 ; Clear Hi (no upper data bits) - .elseif ${frameSize} == 34 - ldi ${dataByteRegHi}, 0 ; Clear Hi (no upper data bits) - .else - ldi TEMP_REG2, 1 - lsl TEMP_REG2, TEMP_REG2, (${frameSize} - 34) - sub TEMP_REG2, TEMP_REG2, 1 - and ${dataByteRegHi}, ${dataByteRegHi}, TEMP_REG2 - .endif -`; - } else { - // MSB first extended: bits accumulated at low end of Hi:Lo pair - // Frame layout: Hi contains upper bits (including start), Lo contains lower 32 bits - // [frameSize-1]=start (in Hi), data spans Hi:Lo, [0]=stop (in Lo) - macroBody += ` ; Extract data (extended MSB): remove stop bit, mask data bits - ; Shift right by 1 across both registers to remove stop bit - lsr ${dataByteRegLo}, ${dataByteRegLo}, 1 - qbbc no_stop_carry?, ${dataByteRegHi}, 0 - set ${dataByteRegLo}, ${dataByteRegLo}, 31 -no_stop_carry?: - lsr ${dataByteRegHi}, ${dataByteRegHi}, 1 - ; Mask to keep only data bits (removes start bit) - ; For frameSize 33: dataBits=31, start bit carried to Lo bit 31, clear it - ; For frameSize 34: dataBits=32, all data in Lo after shift, just clear Hi - ; For frameSize 35+: dataBits=33+, upper bits in Hi = frameSize - 34 - .if ${frameSize} == 33 - clr ${dataByteRegLo}, ${dataByteRegLo}, 31 ; Clear start bit carried to Lo bit 31 - ldi ${dataByteRegHi}, 0 ; Clear Hi (no upper data bits) - .elseif ${frameSize} == 34 - ldi ${dataByteRegHi}, 0 ; Clear Hi (no upper data bits) - .else - ldi TEMP_REG2, 1 - lsl TEMP_REG2, TEMP_REG2, (${frameSize} - 34) - sub TEMP_REG2, TEMP_REG2, 1 - and ${dataByteRegHi}, ${dataByteRegHi}, TEMP_REG2 - .endif -`; - } - } else { - // Standard mode extraction: data in single register (no Hi register) - if (isLSB) { - // LSB first: bits accumulated at high end of register, shift right to align - // After receiving frameSize bits, they are in bits [31 : 32-frameSize] - // We need to: - // 1. Shift right by (32 - frameSize) to align to bit 0 - // 2. Shift right by 1 to remove start bit - // 3. Mask to keep only (frameSize - 2) data bits - // NOTE: Use TEMP_REG2.w0 for shift amount since TEMP_REG1 holds accumulated bits - macroBody += ` ; Extract data: shift to align, remove start bit, mask data bits - ldi TEMP_REG2.w0, 32 - sub TEMP_REG2.w0, TEMP_REG2.w0, ${frameSize} - lsr ${bitAccumReg}, ${bitAccumReg}, TEMP_REG2.w0 ; Align frame to bit 0 - lsr ${dataByteRegLo}, ${bitAccumReg}, 1 ; Remove start bit (shift right by 1) - ; Mask to (frameSize - 2) data bits: mask = (1 << (frameSize-2)) - 1 - ldi TEMP_REG2, 1 - lsl TEMP_REG2, TEMP_REG2, (${frameSize} - 2) - sub TEMP_REG2, TEMP_REG2, 1 - and ${dataByteRegLo}, ${dataByteRegLo}, TEMP_REG2 ; Keep only data bits -`; - } else { - // MSB first: bits accumulated at low end of register - // After receiving frameSize bits, they are in bits [frameSize-1 : 0] - // Frame layout: [frameSize-1]=start, [frameSize-2:1]=data, [0]=stop - // We need to: - // 1. Shift right by 1 to remove stop bit (data now at [frameSize-3:0], start at [frameSize-2]) - // 2. Mask to keep only (frameSize - 2) data bits (removes start bit) - // NOTE: Use TEMP_REG2 for mask calculation since TEMP_REG1 holds accumulated bits - macroBody += ` ; Extract data: remove stop bit, mask data bits (removes start bit) - lsr ${dataByteRegLo}, ${bitAccumReg}, 1 ; Remove stop bit (shift right by 1) - ; Mask to (frameSize - 2) data bits: mask = (1 << (frameSize-2)) - 1 - ldi TEMP_REG2, 1 - lsl TEMP_REG2, TEMP_REG2, (${frameSize} - 2) - sub TEMP_REG2, TEMP_REG2, 1 - and ${dataByteRegLo}, ${dataByteRegLo}, TEMP_REG2 ; Keep only data bits -`; - } - } - - // Note: Data is now available in dataByteRegLo (and dataByteRegHi for extended mode) - // The connected block will use these registers for further processing - - macroBody += ` - ; ========== Disable RX (TRM step 7) ========== - ; Clear rx_en to disable channel - resets all counters and flags - ldi r30.b3, 0x00 -`; - } - - if (pruInstructionMacro == "") { - macroBody += "\n" + " .endm"; - } - - return prefix + macroBody; -} - -function getAIContext() { - return getLongDescription() + ` -## How to Configure (For AI/Scripting) - -This section describes how to programmatically configure the UART RX block in a .syscfg file. - -### Adding a UART RX Instance - -\`\`\`javascript -const uart_rx = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx", {}, false); -const uart_rx1 = uart_rx.addInstance(); -\`\`\` - -### Configuration Parameters - -| Parameter | Type | Valid Values | Default | Description | -|-----------|------|--------------|---------|-------------| -| pruSelect | Integer | 0, 1 | Auto-detected | PRU Selection (0=PRU0, 1=PRU1) - auto-detected from system context (read-only) | -| channel | Integer | 0, 1, 2 | 2 | ENDAT channel for UART RX | -| clockSource | Integer | 0, 1 | 0 | Clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) | -| baudRate | Integer | MHz value | 12 | Baud rate in MHz. Clock source must be divisible by (baudRate × oversample) | -| oversampleSize | Integer | 0, 1, 3, 7 | 7 | Oversample encoding (0=1x, 1=2x, 3=4x, 7=8x) | -| startBitPolarity | Integer | 0, 1 | 1 | Start bit edge detection (0=Falling, 1=Rising) | -| frameSize | Integer | 3-64 | 10 | Total frame bits (dataBits + 2 for start/stop) | -| bitSwap | Boolean | true, false | true | Enable LSB-first reception (true) or MSB-first (false) | - -### Valid Baud Rates -**With 192 MHz clock source and 8x oversample (default):** -192 / (baudRate × 8) must be an integer. Valid baud rates include: -- 24 MHz (clockDivider = 0) -- 12 MHz (clockDivider = 1) -- 8 MHz (clockDivider = 2) -- 6 MHz (clockDivider = 3) -- 4 MHz (clockDivider = 5) -- 3 MHz (clockDivider = 7) -- 2 MHz (clockDivider = 11) -- 1 MHz (clockDivider = 23) - -**With 200 MHz clock source and 8x oversample:** -200 / (baudRate × 8) must be an integer. Valid baud rates include: -- 25 MHz (clockDivider = 0) -- 5 MHz (clockDivider = 4) -- 2.5 MHz (clockDivider = 9) -- 1.25 MHz (clockDivider = 19) - -### Example Configurations - -**Standard 8-bit UART at 12 MHz with 8x oversample (192 MHz clock):** -\`\`\`javascript -uart_rx1.$name = "UART_RX_0"; -uart_rx1.pruSelect = 1; -uart_rx1.channel = 2; -uart_rx1.clockSource = 0; // 192 MHz (UART_CLK) -uart_rx1.baudRate = 12; // 12 MHz baud rate -uart_rx1.oversampleSize = 7; // 8x oversample -uart_rx1.frameSize = 10; // 8 data bits + start + stop = 10 -uart_rx1.bitSwap = true; // LSB first (standard UART) -uart_rx1.startBitPolarity = 0; // Detect falling edge (standard UART) -\`\`\` - -**8-bit UART at 25 MHz using 200 MHz clock:** -\`\`\`javascript -uart_rx1.$name = "UART_RX_200MHz"; -uart_rx1.pruSelect = 1; -uart_rx1.channel = 2; -uart_rx1.clockSource = 1; // 200 MHz (CORE_CLK) -uart_rx1.baudRate = 25; // 25 MHz baud rate (200 / (25 × 8) = 1, divider = 0) -uart_rx1.oversampleSize = 7; // 8x oversample -uart_rx1.frameSize = 10; -uart_rx1.bitSwap = true; -uart_rx1.startBitPolarity = 0; -\`\`\` - -**Extended 31-bit reception on PRU0, Channel 0:** -\`\`\`javascript -uart_rx1.$name = "UART_RX_Extended"; -uart_rx1.pruSelect = 0; -uart_rx1.channel = 0; -uart_rx1.clockSource = 0; // 192 MHz (UART_CLK) -uart_rx1.baudRate = 12; // 12 MHz baud rate -uart_rx1.oversampleSize = 7; // 8x oversample -uart_rx1.frameSize = 33; // 31 data bits + start + stop = 33 (uses extended mode) -uart_rx1.bitSwap = false; // MSB first -uart_rx1.startBitPolarity = 1; // Detect rising edge -\`\`\` - -### Connecting to Other Blocks - -\`\`\`javascript -// Connect UART RX output to downstream processing block -scripting.connect(uart_rx1, "output1", process_block, "input1"); - -// Connect control flow -scripting.connect(prev_block, "next", uart_rx1, "prev"); -scripting.connect(uart_rx1, "next", next_block, "prev"); -\`\`\` - -### Important Notes for Extended Mode (frameSize >= 33, i.e., dataBits >= 31) - -When receiving 31 or more data bits: -- The output register is **8 bytes** (two 32-bit registers) -- Lower 32 bits are in the first output register -- Upper bits are in the second output register -- Blocks connected to output1 will automatically receive the full 64-bit value -`; -} - -function getLongDescription() { - return ` -## UART RX (Hardware-Accelerated via ENDAT) - -### Purpose -Configures the PRU-ICSS ENDAT peripheral hardware for UART reception at high speed (up to 12 Mbaud) and receives one complete UART frame. This block performs hardware initialization, frame reception, bit extraction, and outputs the processed data byte via a dynamically allocated output register. - -### How It Works -1. **Peripheral Mode**: Sets GPCFG to enable peripheral interface mode for the selected PRU -2. **RX Clock Configuration**: Configures clock divider and oversample rate - - RX frequency = Core_Clock / (divider + 1) - - Example: 192 MHz / (1+1) = 96 MHz with 8x oversample = 12 MHz baud rate -3. **Start Bit Detection**: Configures start bit polarity (rising/falling edge) -4. **Frame Configuration**: Sets frame size and bit ordering (LSB/MSB first) -5. **Channel Selection**: Configures one of three available channels (CH0, CH1, or CH2) -6. **Global Reinit**: Resets the ENDAT peripheral hardware state before each reception - - **Important**: Reinit clears all pending/buffered data in the ENDAT peripheral - - This ensures clean state for each frame but means previously buffered frames are lost - - Reinit must complete before RX enable (20-cycle wait loop included) -7. **RX Enable**: Enables reception on the selected channel -8. **Frame Reception**: Polls for valid data, reads oversampled bits, extracts middle sample -9. **Bit Accumulation**: Assembles bits according to LSB/MSB first setting -10. **Data Extraction**: Removes start/stop bits, extracts data byte to output register - -### Output Port -- **output1**: Received data byte (1 byte) - dynamically allocated register - - Connect this port to downstream blocks that need to process the received data - -### Configuration Parameters - -**Channel Selection** (0, 1, or 2) -- Selects which ENDAT channel to use for UART RX -- Each channel can operate independently -- Default: 2 (matches most common configurations) - -**Clock Source** (192 MHz or 200 MHz) -- RX clock source selection -- 192 MHz: ICSSGn_UART_CLK (default) - tested up to 32 MHz baud rate -- 200 MHz: ICSSGn_CORE_CLK - tested up to 20 MHz baud rate -- Configured via PRU0_ED_RX_CLK_SEL bit in ICSSG_PRU0_ED_RX_CFG_REG -- Default: 192 MHz - -**Baud Rate (MHz)** -- Desired UART baud rate in MHz -- Selected clock source must be divisible by (baudRate × oversample) -- Clock divider is automatically calculated: (clockSource / (baudRate × oversample)) - 1 -- Example with 192 MHz: 12 MHz baud with 8x oversample → clockDivider = (192 / (12 × 8)) - 1 = 1 -- Example with 200 MHz: 10 MHz baud with 8x oversample → clockDivider = (200 / (10 × 8)) - 1 = (200/80) - 1 = 1.5 (invalid) -- Default: 12 MHz - -**Oversample Size** (1x, 2x, 4x, 8x) -- Number of samples per bit for robust reception -- Higher oversample = better noise immunity -- 8x oversample means 8 samples per bit, block checks middle sample (bit 4) -- 4x oversample checks bit 2, 2x/1x check bit 0 -- RX baud rate = RX_clock / oversample -- Default: 8x - -**Start Bit Polarity** (Falling=0, Rising=1) -- Defines the edge that indicates start of frame -- Standard UART uses falling edge (idle high → start low) -- Default: Rising (1) - -**Frame Size** (3-32 bits) -- Total number of bits to receive per frame -- Standard UART: 10 bits (1 start + 8 data + 1 stop) -- Maximum supported: 32 bits (30 data bits + start + stop) -- Default: 10 - -**Bit Swap (LSB First)** (Enabled/Disabled) -- Enable: LSB transmitted first (standard UART) -- Disable: MSB transmitted first -- Default: Enabled - -### Calculation Example -- UART Clock: 192 MHz -- Baud Rate: 12 MHz -- Oversample: 8x -- Clock Divider: (192 / (12 × 8)) - 1 = 1 -- RX Clock: 192 / (1+1) = 96 MHz -- Verification: 96 MHz / 8 = 12 MHz ✓ - -### Generated Assembly -\`\`\`assembly -; Configure GPCFG1 for peripheral mode -ldi32 TEMP_REG1, 0x04000000 -ldi TEMP_REG2.w0, ICSS_CFG_GPCFG1 -sbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -; Configure RXCFG (clock divider, oversample, start bit polarity) -ldi TEMP_REG1.w0, 0x000F ; Oversample + polarity -ldi TEMP_REG1.w2, 0x0001 ; Clock divider -ldi TEMP_REG2.w0, ICSS_CFG_PRU1_ENDAT_RXCFG -sbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -; Configure CHx_CFG0 (frame size, bit swap) -ldi TEMP_REG1.w0, 0x000A ; Frame size = 10 -ldi TEMP_REG1.w2, 0x8000 ; Bit swap enabled -ldi TEMP_REG2.w0, ICSS_CFG_PRU1_ENDAT_CH2_CFG0 -sbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -; Global reinit -set r31, r31, 19 -; Wait... - -; Enable RX on channel 2 -set r30, r30, 26 -\`\`\` - -### Performance -- **Configuration Cycles**: ~25-30 cycles (GPCFG1, RXCFG, CHx_CFG0, reinit wait, RX enable) -- **Reception Cycles**: Variable, depends on frame size and baud rate - - Standard 10-bit frame: ~10 iterations of bit polling loop - - Each bit: Wait for valid flag + read + clear + accumulate (~5-10 cycles per bit) -- **Total**: ~50-100 cycles typical for 10-bit frame -- **Max Baud Rate**: Up to 12 Mbaud with proper clock configuration - -### Usage Notes -- This block performs **both configuration and reception** of one UART frame -- Execution continues to next block after frame is received and stored -- **Reinit Side Effects**: - - Global reinit (R31 bit 19) resets TX and RX state machines - - Any data currently being received or buffered will be lost - - Reinit is required before first use and recommended between frames for clean state - - 20-cycle wait ensures reinit completes before RX enable -- Data is output to the dynamically allocated output register (connect output port to downstream blocks) -- Valid flag must be cleared (set R31 bit) after each bit read to prevent overflow -- Hardware automatically deserializes incoming UART frames with oversampling - -### Terminology -- **UART**: Universal Asynchronous Receiver/Transmitter - serial communication protocol -- **ENDAT**: PRU-ICSS peripheral hardware used for high-speed serial reception -- **Baud Rate**: Bits per second transmission rate -- **Oversample**: Multiple samples per bit for noise immunity and bit detection -- **Start Bit**: Single bit that marks beginning of frame (typically falling edge) -- **Stop Bit**: Single bit that marks end of frame (typically high) -- **Frame**: Complete data transmission including start, data, and stop bits -- **LSB First**: Least Significant Bit transmitted first (standard UART) -- **MSB First**: Most Significant Bit transmitted first (non-standard) -- **Bit Swap**: Hardware feature to reverse bit order (LSB↔MSB) -- **Valid Flag**: R31 status bit indicating channel has valid data ready -- **Global Reinit**: Hardware reset of ENDAT TX and RX state machines -- **Channel**: One of three independent ENDAT reception paths (CH0, CH1, CH2) -- **GPCFG**: Global Peripheral Configuration register -- **RXCFG**: Receive Configuration register (clock, oversample, polarity) -- **CHx_CFG0**: Channel Configuration register (frame size, bit swap) -- **Output Register**: Dynamically allocated register where received byte is stored -- **R30**: PRU output register (used for RX enable control) -- **R31**: PRU input register (used for valid flags and data bits) - ---- -`; -} - -exports = { - displayName: "PRU UART RX", - defaultInstanceName: `PRU${PRU_USED}_UART_RX_`, - longDescription: getLongDescription(), - getAIContext: getAIContext, - uiView: "graph", - requiredIncludes: [ - /*TODO : review on how to add include files for the modules*/ - // for a workaround defined the registers in the UART block itself - ], - templates: { - "/pru_blocks/common/pru_syscfg.asm.xdt": null - }, - config: [ - { - name: "$shape", - hidden: true, - default: "square", - }, - { - name: "$topLabel", - hidden: true, - default: "", - }, - // ========== User Configuration Parameters ========== - { - name: "pruSelect", - displayName: "PRU Selection", - description: "Auto-detected from system context (read-only)", - default: getPruNumberFromContext(), - readOnly: true, - options: [ - { name: 0, displayName: "PRU0" }, - { name: 1, displayName: "PRU1" } - ] - }, - { - name: "channel", - displayName: "Channel Selection", - description: "ENDAT channel to use for UART RX (0, 1, or 2)", - default: 2, - options: [ - { name: 0, displayName: "Channel 0" }, - { name: 1, displayName: "Channel 1" }, - { name: 2, displayName: "Channel 2" } - ] - }, - { - name: "clockSource", - displayName: "Clock Source", - description: "RX clock source selection. 192 MHz (ICSSGn_UART_CLK) or 200 MHz (ICSSGn_CORE_CLK)", - default: 0, - options: [ - { name: 0, displayName: "192 MHz (UART_CLK)" }, - { name: 1, displayName: "200 MHz (CORE_CLK)" } - ] - }, - { - name: "baudRate", - displayName: "Baud Rate (MHz)", - description: "Desired UART baud rate in MHz. Clock must be divisible by (baudRate * oversample).", - default: 12, - displayFormat: "dec" - }, - { - name: "clockDivider", - displayName: "Clock Divider (Calculated)", - description: "Calculated clock divider = (clockSource / (baudRate * oversample)) - 1", - default: 1, - readOnly: true, - getValue: (inst) => { - // Get oversample multiplier from encoding (0=1x, 1=2x, 3=4x, 7=8x) - let oversampleMultiplier; - if (inst.oversampleSize === 0) { - oversampleMultiplier = 1; - } else if (inst.oversampleSize === 1) { - oversampleMultiplier = 2; - } else if (inst.oversampleSize === 3) { - oversampleMultiplier = 4; - } else { - oversampleMultiplier = 8; - } - // Get clock frequency based on clock source selection - const uartClkMHz = inst.clockSource === 0 ? 192 : 200; - const baudRateTimesOversample = inst.baudRate * oversampleMultiplier; - if (baudRateTimesOversample === 0 || uartClkMHz % baudRateTimesOversample !== 0) { - return 0; // Invalid, validation will catch this - } - return (uartClkMHz / baudRateTimesOversample) - 1; - } - }, - { - name: "oversampleSize", - displayName: "Oversample Size", - description: "Number of samples per bit. Baud rate = RX_clock / oversample", - default: 7, // 7 = 8x oversample (bits[2:0] encoding) - options: [ - { name: 0, displayName: "1x" }, - { name: 1, displayName: "2x" }, - { name: 3, displayName: "4x" }, - { name: 7, displayName: "8x" } - ] - }, - { - name: "startBitPolarity", - displayName: "Start Bit Polarity", - description: "Edge that indicates start of frame", - default: 1, // 1 = rising edge - options: [ - { name: 0, displayName: "Falling Edge (0)" }, - { name: 1, displayName: "Rising Edge (1)" } - ] - }, - { - name: "frameSize", - displayName: "Frame Size (bits)", - description: "Total bits per frame = dataBits + 2 (start + stop). Range: 3-64 (1-62 data bits)", - default: 10, - range: [3, 64], - displayFormat: "dec" - }, - { - name: "bitSwap", - displayName: "Bit Swap (LSB First)", - description: "Enable LSB-first transmission (standard UART)", - default: true - }, - // ========== Calculated/Hidden Configuration ========== - { - name: "constant1", - hidden: true, - default: "2, 1, 0, 7, 1, 10, 1", - getValue: (inst) => { - // Build the macro parameters string - // Macro signature: m_uart_rx_config dataByteRegLo (output1), dataByteRegHi (output2), channel, clockDiv, rxClkSel, oversample, startBitPol, frameSize, bitSwap - // Note: dataByteRegLo/dataByteRegHi are dynamically allocated as output ports and prepended by register allocator - // Internal registers (bitAccumReg, bitCounterReg, tempSampleReg) use TEMP_REG1/TEMP_REG2 - const channel = inst.channel; - const rxClkSel = inst.clockSource; // 0=192MHz, 1=200MHz - - // Calculate clock divider from baud rate using selected clock source - let oversampleMultiplier; - if (inst.oversampleSize === 0) { - oversampleMultiplier = 1; - } else if (inst.oversampleSize === 1) { - oversampleMultiplier = 2; - } else if (inst.oversampleSize === 3) { - oversampleMultiplier = 4; - } else { - oversampleMultiplier = 8; - } - const uartClkMHz = inst.clockSource === 0 ? 192 : 200; - const baudRateTimesOversample = inst.baudRate * oversampleMultiplier; - const clockDiv = (baudRateTimesOversample === 0 || uartClkMHz % baudRateTimesOversample !== 0) - ? 0 - : (uartClkMHz / baudRateTimesOversample) - 1; - - const oversample = inst.oversampleSize; - const startBitPol = inst.startBitPolarity; - const frameSize = inst.frameSize; - const bitSwap = inst.bitSwap ? 1 : 0; - - return `${channel}, ${clockDiv}, ${rxClkSel}, ${oversample}, ${startBitPol}, ${frameSize}, ${bitSwap}`; - }, - }, - { - name: "opCode", - displayName: "m_uart_rx_config", - default: "m_uart_rx_config", - hidden: true, - getValue: (inst) => { - // Generate macro name based on PRU, bit order, oversample size, and extended mode - // Format: m_uart_rx_config_pru<0|1>__<1x|2x|4x|8x>[_ext] - const pruNum = inst.pruSelect; - const bitOrder = inst.bitSwap ? "lsb" : "msb"; - const dataBits = inst.frameSize - 2; - // Extended mode for dataBits >= 31 (frameSize >= 33) - const isExtended = dataBits >= 31; - let oversampleName; - if (inst.oversampleSize === 0) { - oversampleName = "1x"; - } else if (inst.oversampleSize === 1) { - oversampleName = "2x"; - } else if (inst.oversampleSize === 3) { - oversampleName = "4x"; - } else { - oversampleName = "8x"; - } - const extSuffix = isExtended ? "_ext" : ""; - return `m_uart_rx_config_pru${pruNum}_${bitOrder}_${oversampleName}${extSuffix}`; - }, - }, - { - name: "numOfInputPorts", - default: 0, // No input - this block receives data from UART hardware - hidden: true - }, - { - name: "numOfOutputPorts", - default: 1, - hidden: true - }, - { - name: "output1Size", - default: 8, - hidden: true, - getValue: (inst) => { - // Data bits = frameSize - 2 (remove start and stop bits) - const dataBits = inst.frameSize - 2; - // Extended mode (>=31 data bits) needs 8 bytes (two 32-bit registers) - // Standard mode (<=30 data bits) needs 4 bytes (one 32-bit register) - return dataBits >= 31 ? 8 : 4; - } - }, - { - name: "numOfConstants", - default: 1, - hidden: true - }, - { - name: "uartBitPeriod", - displayName: "UART Bit Period", - description: "Calculated period per bit based on baud rate", - hidden: false, - getValue: (inst) => { - // Bit period = 1 / baudRate (in MHz) = 1000 / baudRate (in ns) - const bitPeriodNs = 1000 / inst.baudRate; - - if (bitPeriodNs >= 1000) { - return (bitPeriodNs / 1000).toFixed(2) + " us"; - } else { - return bitPeriodNs.toFixed(2) + " ns"; - } - }, - default: "N/A" - }, - { - name: "rxOversampleClock", - displayName: "RX Oversample Clock", - description: "RX oversample clock frequency = baudRate * oversample", - hidden: false, - getValue: (inst) => { - // Get oversample multiplier from encoding (0=1x, 1=2x, 3=4x, 7=8x) - let oversampleMultiplier; - if (inst.oversampleSize === 0) { - oversampleMultiplier = 1; - } else if (inst.oversampleSize === 1) { - oversampleMultiplier = 2; - } else if (inst.oversampleSize === 3) { - oversampleMultiplier = 4; - } else { - oversampleMultiplier = 8; - } - const rxClkMHz = inst.baudRate * oversampleMultiplier; - - if (rxClkMHz >= 1) { - return rxClkMHz.toFixed(2) + " MHz"; - } else { - return (rxClkMHz * 1000).toFixed(2) + " kHz"; - } - }, - default: "N/A" - }, - { - name: "noOfCycles", - displayName: "Number Of Cycles", - getValue: (inst) => { - // Approximate cycle count: - // GPCFG1 config: 4 cycles - // RXCFG config: 6 cycles - // CHx_CFG0 config: 8 cycles (with branching) - // Global reinit: 23 cycles (set + wait loop) - // RX enable: 5 cycles (with branching) - // Total: ~46 cycles - return 46; - }, - default: 46 - } - ], - ports: (inst) => { - let ports = []; - // Output port for received data (8 bytes = two 32-bit registers for 64-bit data) - ports.push({ name: "output1", type: "output" }); - ports.push({ name: "prev", type: "PREV" }); - ports.push({ name: "next", type: "NEXT" }); - return ports; - }, - validate, - moduleStatic: { - modules: function(inst) { - return [{ - name: "pru_register_allocator_validator", - moduleName: "/pru_blocks/common/pru_blocks_static_module" - }] - }, - }, - getMacro -} diff --git a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx.syscfg.js deleted file mode 100644 index 19425ca..0000000 --- a/.metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/uart_tx.syscfg.js +++ /dev/null @@ -1,1657 +0,0 @@ -/** - * Helper function to extract PRU number from system context - * @returns {number} PRU number (0 or 1), defaults to 0 if cannot determine - */ -function getPruNumberFromContext() { - const common = system.getScript("/common"); - const coreName = common.getSelfSysCfgCoreName(); - - // coreName format: "icss_g0_pru0" or "icss_g0_pru1" - // Extract the last character which should be the PRU number - if (coreName && coreName.includes("pru")) { - const match = coreName.match(/pru(\d+)$/); - if (match && match[1]) { - return parseInt(match[1]); - } - } - - // Default to PRU0 if unable to determine - return 0; -} -const PRU_USED = getPruNumberFromContext(); -function validate(inst, report) { - // Get clock frequency based on clock source selection - // 0 = 192 MHz (ICSSGn_UART_CLK), 1 = 200 MHz (ICSSGn_CORE_CLK) - const uartClkMHz = inst.clockSource === 0 ? 192 : 200; - - // Check if clock is divisible by baudRate - // Formula: clockDivider = (CLK / baudRate) - 1 - if (uartClkMHz % inst.baudRate !== 0) { - report.logError(`Clock (${uartClkMHz} MHz) is not divisible by baudRate = ${inst.baudRate} MHz. Choose a baud rate where ${uartClkMHz} is divisible by baudRate.`, inst, "baudRate"); - } else { - // Check clock divider range - const clockDivider = (uartClkMHz / inst.baudRate) - 1; - if (clockDivider < 0 || clockDivider > 65535) { - report.logError(`Calculated clock divider (${clockDivider}) is out of range (0-65535). Adjust baud rate.`, inst, "baudRate"); - } - } - - // Validate data bits range (1-62 bits) - // For dataBits <= 29: single-shot mode (frame size <= 31 bits) - // For dataBits > 29: continuous mode (frame size <= 64 bits) - if (inst.dataBits < 1 || inst.dataBits > 62) { - report.logError("Data bits must be between 1 and 62", inst, "dataBits"); - } - - // Port connection warnings - numOfInputPorts is dynamically set based on dataBits - for (let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - if (inst["input" + iterator.toString()].length == 0) { - report.logWarning("input" + iterator.toString() + " port not connected to output port", inst); - } - } -} - -/** - * Returns guarded .set/.asg definitions for ICSS_CFG register symbols. - * Emitted once at the top of the generated macro (before .macro line) so the - * assembler has the symbols available even when icss_cfg_regs.inc / icss_regs.inc - * are not explicitly included by the user. - */ -function getIcssCfgDefinitions() { - return `\ -; ========== ICSS CFG register definitions (auto-generated, guarded) ========== - .if !$isdefed("ICSS_CFG") - .asg c4, ICSS_CFG - .endif - .if !$isdefed("ICSS_CFG_GPCFG0") -ICSS_CFG_GPCFG0 .set 0x0008 - .endif - .if !$isdefed("ICSS_CFG_GPCFG1") -ICSS_CFG_GPCFG1 .set 0x000C - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_TXCFG") -ICSS_CFG_PRU0_ENDAT_TXCFG .set 0x00E4 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_TXCFG") -ICSS_CFG_PRU1_ENDAT_TXCFG .set 0x0104 - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH0_CFG0") -ICSS_CFG_PRU0_ENDAT_CH0_CFG0 .set 0x00E8 - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH1_CFG0") -ICSS_CFG_PRU0_ENDAT_CH1_CFG0 .set 0x00F0 - .endif - .if !$isdefed("ICSS_CFG_PRU0_ENDAT_CH2_CFG0") -ICSS_CFG_PRU0_ENDAT_CH2_CFG0 .set 0x00F8 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH0_CFG0") -ICSS_CFG_PRU1_ENDAT_CH0_CFG0 .set 0x0108 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH1_CFG0") -ICSS_CFG_PRU1_ENDAT_CH1_CFG0 .set 0x0110 - .endif - .if !$isdefed("ICSS_CFG_PRU1_ENDAT_CH2_CFG0") -ICSS_CFG_PRU1_ENDAT_CH2_CFG0 .set 0x0118 - .endif -; ============================================================================ -`; -} - -/** - * Returns the body of the UART TX configuration macro for single-shot mode (1-29 bits) - * @param {string} pruInstructionMacro - The macro instruction string - * @param {string} opCode - The operation code (macro name) - * @returns {string} The full macro body as a string with parameters replaced - */ -function getMacroSingleShot(pruInstructionMacro, opCode) { - let macroBody = ""; - - // Extract parameters from the pruInstructionMacro - // Parameter order: dataRegLo, channel, clockDiv, txClkSel, dataBits - // (dataRegHi is NOT used in single-shot mode, so not included in macro signature) - const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); - const macroName = parts[0] || opCode; - const dataRegLo = parts[1] || "dataRegLo"; // Input1: Lower 32 bits - const channel = parts[2] || "channel"; - const clockDiv = parts[3] || "clockDiv"; - const txClkSel = parts[4] || "txClkSel"; // TX clock source: 0=192MHz, 1=200MHz - const dataBits = parts[5] || "dataBits"; - - // Extract variant information from macro name - const isPRU0 = macroName.includes("_pru0_"); - const isLSB = macroName.includes("_lsb_"); - const startBit1 = macroName.includes("_start1"); - - // Extract number of FIFO bytes from macro name (1-4 for single-shot) - const numBytesMatch = macroName.match(/_(\d)byte_/); - const numFifoBytes = numBytesMatch ? parseInt(numBytesMatch[1]) : 2; - - // Determine actual values - const pruNum = isPRU0 ? "0" : "1"; - const gpcfgReg = isPRU0 ? "ICSS_CFG_GPCFG0" : "ICSS_CFG_GPCFG1"; - const txcfgReg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_TXCFG" : "ICSS_CFG_PRU1_ENDAT_TXCFG"; - const ch0cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH0_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH0_CFG0"; - const ch1cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH1_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH1_CFG0"; - const ch2cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH2_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH2_CFG0"; - const startBitPol = startBit1 ? "1" : "0"; - // Check for explicit stop bit suffix, otherwise derive from start bit (default: opposite) - const hasExplicitStop0 = macroName.includes("_stop0"); - const hasExplicitStop1 = macroName.includes("_stop1"); - const stopBitPol = hasExplicitStop0 ? "0" : (hasExplicitStop1 ? "1" : (startBit1 ? "0" : "1")); - - if (pruInstructionMacro == "") { - macroBody = macroName + "\t" + ".macro " + dataRegLo + ", " + channel + ", " + clockDiv + ", " + txClkSel + ", " + dataBits + "\n"; - macroBody += `\t; Parameters: -\t; dataRegLo - Register containing data (input1, up to 29 bits) -\t; channel - ENDAT channel (0, 1, or 2) -\t; clockDiv - TX clock divider (0-65535): TX_freq = core_clk / (clockDiv + 1) -\t; txClkSel - TX clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) -\t; dataBits - Number of data bits to transmit (1-29 for single-shot) -\t; Configuration: PRU${pruNum}, ${isLSB ? "LSB" : "MSB"} first, start bit=${startBitPol}, stop bit=${stopBitPol} -\t; Mode: Single-shot (frame size <= 31 bits) -\t; FIFO loads: ${numFifoBytes} bytes -`; - } - - macroBody += ` -\t; ========== ENDAT Peripheral Reset Sequence ========== -\t; Disable RX before reinit, then de-assert rx_en after (TRM-mandated sequence) -\tldi r30.b3, 0x00 ; 1. Disable rx_en for all channels -\tset r31, r31, 19 ; 2. Global Reinit to clear FIFO and state machines - -\t; ========== Wait for Reinit Complete ========== -\t; Poll busy bit for selected channel (1=active, 0=done) -\t.if ${channel} == 0 -wait_reinit?: -\tqbbs wait_reinit?, r31, 5 -\t.elseif ${channel} == 1 -wait_reinit?: -\tqbbs wait_reinit?, r31, 13 -\t.else -wait_reinit?: -\tqbbs wait_reinit?, r31, 21 -\t.endif -\tldi r30.b3, 0x00 ; 3. De-assert rx_en after reinit confirmed complete - -\t; ========== Configure GPCFG${pruNum} for PRU${pruNum} Peripheral Mode ========== -\tldi32 TEMP_REG1, 0x04000000 -\tldi TEMP_REG2.w0, ${gpcfgReg} -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Configure TX Clock ========== -\t; [31:16] = clockDiv, [4] = TX_CLK_SEL (0=192MHz, 1=200MHz) -\tldi TEMP_REG1.w0, (${txClkSel} << 4) -\tldi TEMP_REG1.w2, ${clockDiv} -\tldi TEMP_REG2.w0, ${txcfgReg} -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Configure Channel CFG0 Register ========== -\t; [31] = bitSwap, [15:11] = frameSize = dataBits + 2 -\tldi TEMP_REG1.w0, (${dataBits} + 2) << 11 -\tldi TEMP_REG1.w2, 0 -`; - - if (isLSB) { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set bit 31 (bitSwap=1, LSB first) -`; - } - - macroBody += `\t.if ${channel} == 0 -\tldi TEMP_REG2.w0, ${ch0cfg0Reg} -\t.elseif ${channel} == 1 -\tldi TEMP_REG2.w0, ${ch1cfg0Reg} -\t.else -\tldi TEMP_REG2.w0, ${ch2cfg0Reg} -\t.endif -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Select Channel ========== -\t.if ${channel} == 0 -\tldi r30.w2, 0x0018 ; clk_mode=3 (bits[20:19]=0b11, stop high on TX), ch=0 -\t.elseif ${channel} == 1 -\tldi r30.w2, 0x0019 ; clk_mode=3, ch=1 -\t.else -\tldi r30.w2, 0x001A ; clk_mode=3, ch=2 -\t.endif - -\t; ========== Construct Frame ========== -\tmov TEMP_REG1, ${dataRegLo} -`; - - if (isLSB) { - // LSB mode: Frame layout after shift: start@0, data@[dataBits:1], stop@(dataBits+1) - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, ${dataBits} ; Set stop bit (will be at dataBits+1 after shift) -`; - } else { - macroBody += `\tclr TEMP_REG1, TEMP_REG1, ${dataBits} ; Clear stop bit (will be at dataBits+1 after shift) -`; - } - macroBody += `\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift left for start bit at position 0 -`; - if (startBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set start bit at position 0 -`; - } - // Note: For start bit = 0, bit 0 is already 0 after left shift - } else { - // MSB mode: Frame layout: start@31, data@[30:(31-dataBits)], stop@(31-dataBits-1) - macroBody += `\tlsl TEMP_REG1, TEMP_REG1, (31 - ${dataBits}) ; Align data to MSB -`; - if (startBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set start bit at position 31 -`; - } else { - macroBody += `\tclr TEMP_REG1, TEMP_REG1, 31 ; Clear start bit at position 31 -`; - } - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, (31 - ${dataBits} - 1) ; Set stop bit -`; - } else { - macroBody += `\tclr TEMP_REG1, TEMP_REG1, (31 - ${dataBits} - 1) ; Clear stop bit -`; - } - } - - macroBody += ` -\t; ========== Load FIFO ========== -`; - - if (isLSB) { - macroBody += `\tmov r30.b0, TEMP_REG1.b0 -`; - if (numFifoBytes >= 2) macroBody += `\tmov r30.b0, TEMP_REG1.b1 -`; - if (numFifoBytes >= 3) macroBody += `\tmov r30.b0, TEMP_REG1.b2 -`; - if (numFifoBytes >= 4) macroBody += `\tmov r30.b0, TEMP_REG1.b3 -`; - } else { - macroBody += `\tmov r30.b0, TEMP_REG1.b3 -`; - if (numFifoBytes >= 2) macroBody += `\tmov r30.b0, TEMP_REG1.b2 -`; - if (numFifoBytes >= 3) macroBody += `\tmov r30.b0, TEMP_REG1.b1 -`; - if (numFifoBytes >= 4) macroBody += `\tmov r30.b0, TEMP_REG1.b0 -`; - } - - macroBody += ` -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -\t; ========== Wait for TX Complete ========== -\t.if ${channel} == 0 -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 5 -\t.elseif ${channel} == 1 -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 13 -\t.else -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 21 -\t.endif -`; - - if (pruInstructionMacro == "") { - macroBody += "\n .endm"; - } - - return macroBody; -} - -/** - * Returns the body of the UART TX configuration macro for continuous mode (30-62 bits) - * MSB-first only for now - * @param {string} pruInstructionMacro - The macro instruction string - * @param {string} opCode - The operation code (macro name) - * @returns {string} The full macro body as a string with parameters replaced - */ -function getMacroContinuous(pruInstructionMacro, opCode) { - let macroBody = ""; - - // Extract parameters from the pruInstructionMacro - // Parameter order: dataRegLo, dataRegHi, channel, clockDiv, txClkSel, dataBits - const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); - const macroName = parts[0] || opCode; - const dataRegLo = parts[1] || "dataRegLo"; // Input1: Lower 32 bits - const dataRegHi = parts[2] || "dataRegHi"; // Input2: Upper 32 bits - const channel = parts[3] || "channel"; - const clockDiv = parts[4] || "clockDiv"; - const txClkSel = parts[5] || "txClkSel"; // TX clock source: 0=192MHz, 1=200MHz - const dataBits = parts[6] || "dataBits"; - - // Extract variant information from macro name - const isPRU0 = macroName.includes("_pru0_"); - const isLSB = macroName.includes("_lsb_"); - const startBit1 = macroName.includes("_start1"); - - // Extract number of FIFO bytes from macro name (5-8 for continuous) - const numBytesMatch = macroName.match(/_(\d)byte_/); - const numFifoBytes = numBytesMatch ? parseInt(numBytesMatch[1]) : 5; - - // Determine actual values - const pruNum = isPRU0 ? "0" : "1"; - const gpcfgReg = isPRU0 ? "ICSS_CFG_GPCFG0" : "ICSS_CFG_GPCFG1"; - const txcfgReg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_TXCFG" : "ICSS_CFG_PRU1_ENDAT_TXCFG"; - const ch0cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH0_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH0_CFG0"; - const ch1cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH1_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH1_CFG0"; - const ch2cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH2_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH2_CFG0"; - const startBitPol = startBit1 ? "1" : "0"; - // Check for explicit stop bit suffix, otherwise derive from start bit (default: opposite) - const hasExplicitStop0 = macroName.includes("_stop0"); - const hasExplicitStop1 = macroName.includes("_stop1"); - const stopBitPol = hasExplicitStop0 ? "0" : (hasExplicitStop1 ? "1" : (startBit1 ? "0" : "1")); - - // Number of bytes to load via continuous polling (after initial 4 bytes) - const continuousBytes = numFifoBytes - 4; - - if (pruInstructionMacro == "") { - macroBody = macroName + "\t" + ".macro " + dataRegLo + ", " + dataRegHi + ", " + channel + ", " + clockDiv + ", " + txClkSel + ", " + dataBits + "\n"; - macroBody += `\t; Parameters: -\t; dataRegLo - Register containing lower 32 bits of data (input1) -\t; dataRegHi - Register containing upper bits of data (input2) -\t; channel - ENDAT channel (0, 1, or 2) -\t; clockDiv - TX clock divider (0-65535): TX_freq = core_clk / (clockDiv + 1) -\t; txClkSel - TX clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) -\t; dataBits - Number of data bits to transmit (30-62 for continuous) -\t; Configuration: PRU${pruNum}, ${isLSB ? "LSB" : "MSB"} first, start bit=${startBitPol}, stop bit=${stopBitPol} -\t; Mode: Continuous (frame size = 0, total bytes = ${numFifoBytes}) -\t; Initial FIFO load: 4 bytes, Continuous load: ${continuousBytes} bytes -`; - } - - macroBody += ` -\t; ========== ENDAT Peripheral Reset Sequence ========== -\t; Disable RX before reinit, then de-assert rx_en after (TRM-mandated sequence) -\tldi r30.b3, 0x00 ; 1. Disable rx_en for all channels -\tset r31, r31, 19 ; 2. Global Reinit to clear FIFO and state machines - -\t; ========== Wait for Reinit Complete ========== -\t; Poll busy bit for selected channel (1=active, 0=done) -\t.if ${channel} == 0 -wait_reinit?: -\tqbbs wait_reinit?, r31, 5 -\t.elseif ${channel} == 1 -wait_reinit?: -\tqbbs wait_reinit?, r31, 13 -\t.else -wait_reinit?: -\tqbbs wait_reinit?, r31, 21 -\t.endif -\tldi r30.b3, 0x00 ; 3. De-assert rx_en after reinit confirmed complete - -\t; ========== Configure GPCFG${pruNum} for PRU${pruNum} Peripheral Mode ========== -\tldi32 TEMP_REG1, 0x04000000 -\tldi TEMP_REG2.w0, ${gpcfgReg} -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Configure TX Clock ========== -\t; [31:16] = clockDiv, [4] = TX_CLK_SEL (0=192MHz, 1=200MHz) -\tldi TEMP_REG1.w0, (${txClkSel} << 4) -\tldi TEMP_REG1.w2, ${clockDiv} -\tldi TEMP_REG2.w0, ${txcfgReg} -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Configure Channel CFG0 Register ========== -\t; [31] = bitSwap, [15:11] = frameSize = 0 (continuous mode) -\tldi TEMP_REG1.w0, 0 ; Frame size = 0 for continuous mode -\tldi TEMP_REG1.w2, 0 -`; - - if (isLSB) { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set bit 31 (bitSwap=1, LSB first) -`; - } - - macroBody += `\t.if ${channel} == 0 -\tldi TEMP_REG2.w0, ${ch0cfg0Reg} -\t.elseif ${channel} == 1 -\tldi TEMP_REG2.w0, ${ch1cfg0Reg} -\t.else -\tldi TEMP_REG2.w0, ${ch2cfg0Reg} -\t.endif -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Select Channel ========== -\t; Use ldi to r30.w2 to avoid read-modify-write on r30.b0 (FIFO port) -\t.if ${channel} == 0 -\tldi r30.w2, 0x0018 ; clk_mode=3 (bits[20:19]=0b11, stop high on TX), ch=0 -\t.elseif ${channel} == 1 -\tldi r30.w2, 0x0019 ; clk_mode=3, ch=1 -\t.else -\tldi r30.w2, 0x001A ; clk_mode=3, ch=2 -\t.endif - -`; - - // Frame construction for continuous mode (MSB-first) - // Following the pattern from main.asm: - // - dataRegHi contains upper bits, dataRegLo contains lower 32 bits - // - For MSB mode: start bit at position 31 of first register - // - Data flows from MSB of dataRegHi down through dataRegLo - // - Stop bit at calculated position in second register - - if (isLSB) { - // LSB-first continuous mode (>32 bits only, 30-32 handled by getMacroSpecificBits) - // Frame layout: [0]=start, [1..dataBits]=data, [dataBits+1]=stop - // FIFO loads from byte 0 upward (low bytes first) - // Use TEMP_REG1 for lower frame, TEMP_REG2 for upper frame - - macroBody += `\t; ========== Construct Frame (LSB First, Continuous >32-bit) ========== -\t; Frame: start@0, data@[1..dataBits], stop@(dataBits+1) -\t; Total frame bits = dataBits + 2 = ${dataBits} + 2 -\t; -\t; >32-bit case: data spans dataRegLo and dataRegHi -\t; Copy data to TEMP registers for manipulation -\tmov TEMP_REG1, ${dataRegLo} ; Lower 32 bits -\tmov TEMP_REG2, ${dataRegHi} ; Upper bits -\t; -\t; 64-bit left shift by 1 to make room for start bit at position 0 -\t; Save bit 31 of TEMP_REG1, shift both registers left, OR saved bit into TEMP_REG2 -\tlsr ${dataRegLo}, TEMP_REG1, 31 ; Save bit 31 into dataRegLo (temp use) -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift lower left by 1 -\tlsl TEMP_REG2, TEMP_REG2, 1 ; Shift upper left by 1 -\tor TEMP_REG2, TEMP_REG2, ${dataRegLo} ; OR saved bit into upper reg bit 0 - -`; - if (startBitPol === "1") { - macroBody += `\t; Set start bit at position 0 -\tset TEMP_REG1, TEMP_REG1, 0 -`; - } - // Note: For start bit = 0, bit 0 is already 0 after left shift - - // Stop bit position = dataBits + 1 (after shifting) - // If dataBits + 1 < 32, stop bit is in TEMP_REG1 - // If dataBits + 1 >= 32, stop bit is in TEMP_REG2 at position (dataBits + 1 - 32) - if (stopBitPol === "1") { - macroBody += `\t; Set stop bit at position (dataBits + 1) -\t.if (${dataBits} + 1) < 32 -\tset TEMP_REG1, TEMP_REG1, (${dataBits} + 1) -\t.else -\tset TEMP_REG2, TEMP_REG2, (${dataBits} + 1 - 32) -\t.endif -`; - } else { - macroBody += `\t; Clear stop bit at position (dataBits + 1) -\t.if (${dataBits} + 1) < 32 -\tclr TEMP_REG1, TEMP_REG1, (${dataBits} + 1) -\t.else -\tclr TEMP_REG2, TEMP_REG2, (${dataBits} + 1 - 32) -\t.endif -`; - } - - macroBody += ` -\t; ========== Load Initial 4 Bytes to FIFO (LSB First) ========== -\tmov r30.b0, TEMP_REG1.b0 -\tmov r30.b0, TEMP_REG1.b1 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b3 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -`; - // Load remaining bytes via continuous polling - const byteNamesLSB = ["TEMP_REG2.b0", "TEMP_REG2.b1", "TEMP_REG2.b2", "TEMP_REG2.b3"]; - for (let i = 0; i < continuousBytes; i++) { - const byteNum = 5 + i; - macroBody += `\t; ========== Load Byte ${byteNum} via Continuous Polling ========== -wait_fifo_for_byte${byteNum}?: -\tmov ${dataRegLo}, r31 -\tlsr ${dataRegLo}, ${dataRegLo}, 2 -\tand ${dataRegLo}, ${dataRegLo}, 0x7 -\tqblt wait_fifo_for_byte${byteNum}?, ${dataRegLo}, 2 ; Wait until FIFO <= 2 -\tmov r30.b0, ${byteNamesLSB[i]} - -`; - } - - } else { - // MSB-first continuous mode (>32 bits only, 30-32 handled by getMacroSpecificBits) - // Use TEMP_REG1 for first 32 bits of frame, TEMP_REG2 for remaining bits - // - // Frame layout (for dataBits data bits): - // Total frame = dataBits + 2 bits (start + data + stop) - // Transmitted MSB first: start, data[dataBits-1], data[dataBits-2], ..., data[0], stop - // - // For transmission, we pack into TEMP_REG1 (first 32 bits) and TEMP_REG2 (remaining bits) - // TEMP_REG1.b3 transmitted first, then b2, b1, b0, then TEMP_REG2.b3, b2, b1, b0 - // - // Input: - // dataRegHi: upper (dataBits - 32) bits of data in positions [(dataBits-33):0] - // dataRegLo: lower 32 bits of data in positions [31:0] - // - // Frame bit positions (MSB = bit 63 transmitted first): - // Bit (dataBits+1): start bit (at position 31 of TEMP_REG1) - // Bits dataBits..1: data bits - // Bit 0: stop bit - - macroBody += `\t; ========== Construct Frame (MSB First, Continuous >32-bit) ========== -\t; Frame: start + ${dataBits} data bits + stop = ${dataBits} + 2 bits total -\t; Input: dataRegHi[(${dataBits}-33):0] = upper ${dataBits}-32 bits, dataRegLo[31:0] = lower 32 bits -\t; -\t; TEMP_REG1 bit layout: [31]=start, [30:0]=data[${dataBits}-1:${dataBits}-31] -\t; TEMP_REG2 bit layout: [31:(64-${dataBits})]=data[${dataBits}-32:0], [...:0]=padding+stop -\t; -\t; Step 1: Clear TEMP_REG1 and TEMP_REG2 -\tldi TEMP_REG1, 0 -\tldi TEMP_REG2, 0 -`; - - if (startBitPol === "1") { - macroBody += ` -\t; Step 2: Set start bit at position 31 of TEMP_REG1 -\tset TEMP_REG1, TEMP_REG1, 31 -`; - } - - macroBody += ` -\t; Step 3: Place upper data bits (dataRegHi) into TEMP_REG1 -\t; dataRegHi has (${dataBits} - 32) bits in positions [(${dataBits}-33):0] -\t; These should go to TEMP_REG1 positions [30:(31-(${dataBits}-32))] = [30:(63-${dataBits})] -\t; Shift left by (63 - ${dataBits}) to align MSB of dataRegHi at bit 30 -\tmov TEMP_REG2, ${dataRegHi} -\tlsl TEMP_REG2, TEMP_REG2, (63 - ${dataBits}) -\tor TEMP_REG1, TEMP_REG1, TEMP_REG2 - -\t; Step 4: Place top bits of dataRegLo into TEMP_REG1 -\t; We need (64 - ${dataBits} - 1) = (63 - ${dataBits}) bits from top of dataRegLo -\t; These are dataRegLo[31:(${dataBits}-31)] going to TEMP_REG1[(62-${dataBits}):0] -\t; Shift dataRegLo right by (${dataBits} - 31) to position these bits at LSB -\tmov TEMP_REG2, ${dataRegLo} -\tlsr TEMP_REG2, TEMP_REG2, (${dataBits} - 31) -\tor TEMP_REG1, TEMP_REG1, TEMP_REG2 - -\t; Step 5: Place remaining bits of dataRegLo into TEMP_REG2 -\t; Remaining bits are dataRegLo[(${dataBits}-32):0] = (${dataBits} - 31) bits -\t; These go to TEMP_REG2[31:(32-(${dataBits}-31))] = [31:(63-${dataBits})] -\t; Shift left by (63 - ${dataBits}) to align at MSB of TEMP_REG2 -\t; But also need to leave room for stop bit at the end -\t; Actually: shift left by (32 - (${dataBits} - 31)) = (63 - ${dataBits}) -\tmov TEMP_REG2, ${dataRegLo} -\tlsl TEMP_REG2, TEMP_REG2, (63 - ${dataBits}) -`; - - if (stopBitPol === "1") { - macroBody += ` -\t; Step 6: Set stop bit -\t; Stop bit position in TEMP_REG2 = (63 - ${dataBits}) - 1 = (62 - ${dataBits}) -\t; For ${dataBits}=62: position = 0, for ${dataBits}=33: position = 29 -\tset TEMP_REG2, TEMP_REG2, (62 - ${dataBits}) -`; - } else { - macroBody += ` -\t; Step 6: Clear stop bit at position (62 - ${dataBits}) -\tclr TEMP_REG2, TEMP_REG2, (62 - ${dataBits}) -`; - } - - macroBody += ` -\t; ========== Load Initial 4 Bytes to FIFO (MSB First) ========== -\tmov r30.b0, TEMP_REG1.b3 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b1 -\tmov r30.b0, TEMP_REG1.b0 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -`; - // Load remaining bytes via continuous polling (MSB first order: b3, b2, b1, b0) - const byteNamesMSB = ["TEMP_REG2.b3", "TEMP_REG2.b2", "TEMP_REG2.b1", "TEMP_REG2.b0"]; - for (let i = 0; i < continuousBytes; i++) { - const byteNum = 5 + i; - macroBody += `\t; ========== Load Byte ${byteNum} via Continuous Polling ========== -wait_fifo_for_byte${byteNum}?: -\tmov TEMP_REG1, r31 -\tlsr TEMP_REG1, TEMP_REG1, 2 -\tand TEMP_REG1, TEMP_REG1, 0x7 -\tqblt wait_fifo_for_byte${byteNum}?, TEMP_REG1, 2 ; Wait until FIFO <= 2 -\tmov r30.b0, ${byteNamesMSB[i]} - -`; - } - } - - // Wait for FIFO empty and TX complete - macroBody += `\t; ========== Wait for FIFO Empty ========== -wait_fifo_empty?: -\tmov TEMP_REG1, r31 -\tlsr TEMP_REG1, TEMP_REG1, 2 -\tand TEMP_REG1, TEMP_REG1, 0x7 -\tqbne wait_fifo_empty?, TEMP_REG1, 0 - -\t; ========== Wait for TX Complete ========== -\t.if ${channel} == 0 -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 5 -\t.elseif ${channel} == 1 -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 13 -\t.else -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 21 -\t.endif -`; - - if (pruInstructionMacro == "") { - macroBody += "\n .endm"; - } - - return macroBody; -} - -/** - * Returns the body of the UART TX configuration macro - * Dispatches to single-shot, continuous, or specific bit-size mode based on macro name - */ -function getMacro(pruInstructionMacro, opCode) { - // When generating the macro definition (not an inline call), prepend the - // guarded ICSS_CFG register definitions so they are available to the assembler. - const prefix = (pruInstructionMacro === "") ? getIcssCfgDefinitions() : ""; - - // Check for specific bit-size macros (30, 31, 32 bits) - const bitSizeMatch = opCode.match(/_(\d+)bit_/); - if (bitSizeMatch) { - const bitSize = parseInt(bitSizeMatch[1]); - if (bitSize >= 30 && bitSize <= 32) { - return prefix + getMacroSpecificBits(pruInstructionMacro, opCode, bitSize); - } - } - - // Extract number of bytes from macro name to determine mode - const numBytesMatch = opCode.match(/_(\d)byte_/); - const numFifoBytes = numBytesMatch ? parseInt(numBytesMatch[1]) : 2; - - if (numFifoBytes <= 4) { - return prefix + getMacroSingleShot(pruInstructionMacro, opCode); - } else { - return prefix + getMacroContinuous(pruInstructionMacro, opCode); - } -} - -/** - * Returns the body of the UART TX macro for specific bit sizes (30, 31, 32 bits) - * These need special handling because all data is in dataRegLo only - */ -function getMacroSpecificBits(pruInstructionMacro, opCode, bitSize) { - let macroBody = ""; - - // Extract parameters from the pruInstructionMacro - // For 30-32 bit macros, format is: macroName dataRegLo, channel, clockDiv, txClkSel - // (dataRegHi is not used since all data fits in dataRegLo) - const parts = pruInstructionMacro.trim().split(/\s*,\s*|\s+/); - const macroName = parts[0] || opCode; - const dataRegLo = parts[1] || "dataRegLo"; - const channel = parts[2] || "channel"; - const clockDiv = parts[3] || "clockDiv"; - const txClkSel = parts[4] || "txClkSel"; // TX clock source: 0=192MHz, 1=200MHz - - // Extract variant information from macro name - const isPRU0 = macroName.includes("_pru0_"); - const isLSB = macroName.includes("_lsb_"); - const startBit1 = macroName.includes("_start1"); - - // Determine actual values - const pruNum = isPRU0 ? "0" : "1"; - const gpcfgReg = isPRU0 ? "ICSS_CFG_GPCFG0" : "ICSS_CFG_GPCFG1"; - const txcfgReg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_TXCFG" : "ICSS_CFG_PRU1_ENDAT_TXCFG"; - const ch0cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH0_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH0_CFG0"; - const ch1cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH1_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH1_CFG0"; - const ch2cfg0Reg = isPRU0 ? "ICSS_CFG_PRU0_ENDAT_CH2_CFG0" : "ICSS_CFG_PRU1_ENDAT_CH2_CFG0"; - const startBitPol = startBit1 ? "1" : "0"; - // Check for explicit stop bit suffix, otherwise derive from start bit (default: opposite) - const hasExplicitStop0 = macroName.includes("_stop0"); - const hasExplicitStop1 = macroName.includes("_stop1"); - const stopBitPol = hasExplicitStop0 ? "0" : (hasExplicitStop1 ? "1" : (startBit1 ? "0" : "1")); - - const frameSize = bitSize + 2; - const numFifoBytes = Math.ceil(frameSize / 8); - - if (pruInstructionMacro == "") { - macroBody = macroName + "\t" + ".macro " + dataRegLo + ", " + channel + ", " + clockDiv + ", " + txClkSel + "\n"; - macroBody += `\t; Parameters: -\t; dataRegLo - Register containing ${bitSize} bits of data (input1) -\t; channel - ENDAT channel (0, 1, or 2) -\t; clockDiv - TX clock divider (0-65535): TX_freq = core_clk / (clockDiv + 1) -\t; txClkSel - TX clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) -\t; Configuration: PRU${pruNum}, ${isLSB ? "LSB" : "MSB"} first, start bit=${startBitPol}, stop bit=${stopBitPol} -\t; Mode: Continuous (${bitSize}-bit specific), frame size = ${frameSize} bits, ${numFifoBytes} bytes -`; - } - - // Common configuration section - macroBody += ` -\t; ========== ENDAT Peripheral Reset Sequence ========== -\t; Disable RX before reinit, then de-assert rx_en after (TRM-mandated sequence) -\tldi r30.b3, 0x00 ; 1. Disable rx_en for all channels -\tset r31, r31, 19 ; 2. Global Reinit to clear FIFO and state machines - -\t; ========== Wait for Reinit Complete ========== -\t; Poll busy bit for selected channel (1=active, 0=done) -\t.if ${channel} == 0 -wait_reinit?: -\tqbbs wait_reinit?, r31, 5 -\t.elseif ${channel} == 1 -wait_reinit?: -\tqbbs wait_reinit?, r31, 13 -\t.else -wait_reinit?: -\tqbbs wait_reinit?, r31, 21 -\t.endif -\tldi r30.b3, 0x00 ; 3. De-assert rx_en after reinit confirmed complete - -\t; ========== Configure GPCFG${pruNum} for PRU${pruNum} Peripheral Mode ========== -\tldi32 TEMP_REG1, 0x04000000 -\tldi TEMP_REG2.w0, ${gpcfgReg} -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Configure TX Clock ========== -\t; [31:16] = clockDiv, [4] = TX_CLK_SEL (0=192MHz, 1=200MHz) -\tldi TEMP_REG1.w0, (${txClkSel} << 4) -\tldi TEMP_REG1.w2, ${clockDiv} -\tldi TEMP_REG2.w0, ${txcfgReg} -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Configure Channel CFG0 Register ========== -\t; [31] = bitSwap, [15:11] = frameSize = 0 (continuous mode) -\tldi TEMP_REG1.w0, 0 ; Frame size = 0 for continuous mode -\tldi TEMP_REG1.w2, 0 -`; - - if (isLSB) { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set bit 31 (bitSwap=1, LSB first) -`; - } - - macroBody += `\t.if ${channel} == 0 -\tldi TEMP_REG2.w0, ${ch0cfg0Reg} -\t.elseif ${channel} == 1 -\tldi TEMP_REG2.w0, ${ch1cfg0Reg} -\t.else -\tldi TEMP_REG2.w0, ${ch2cfg0Reg} -\t.endif -\tsbco &TEMP_REG1, ICSS_CFG, TEMP_REG2.w0, 4 - -\t; ========== Select Channel ========== -\t; Use ldi to r30.w2 to avoid read-modify-write on r30.b0 (FIFO port) -\t.if ${channel} == 0 -\tldi r30.w2, 0x0018 ; clk_mode=3 (bits[20:19]=0b11, stop high on TX), ch=0 -\t.elseif ${channel} == 1 -\tldi r30.w2, 0x0019 ; clk_mode=3, ch=1 -\t.else -\tldi r30.w2, 0x001A ; clk_mode=3, ch=2 -\t.endif - -`; - - // Frame construction based on bit size and bit order - // NOTE: Global Reinit is placed INSIDE each case to match working main.asm timing - if (isLSB) { - if (bitSize === 30) { - // 30 bits LSB: frame = 32 bits, all fits in TEMP_REG1 - // Data bits [29:0] -> shifted to [30:1], start at bit 0, stop at bit 31 - macroBody += `\t; ========== Construct Frame (LSB First, 30-bit) ========== -\t; Frame: start@0, data[29:0]@[30:1], stop@31 -\t; Total: 32 bits = 4 bytes -\tmov TEMP_REG1, ${dataRegLo} ; 30 bits of data (bits [29:0]) -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift left by 1 for start bit -`; - if (startBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set start bit at position 0 -`; - } - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set stop bit at position 31 -`; - } else { - macroBody += `\tclr TEMP_REG1, TEMP_REG1, 31 ; Clear stop bit at position 31 -`; - } - macroBody += ` -\t; ========== Load 4 Bytes to FIFO (LSB First) ========== -\tmov r30.b0, TEMP_REG1.b0 -\tmov r30.b0, TEMP_REG1.b1 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b3 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -`; - } else if (bitSize === 31) { - // 31 bits LSB: frame = 33 bits - // Data bits [30:0] -> shifted to [31:1], start at bit 0, stop at bit 32 (TEMP_REG2) - macroBody += `\t; ========== Construct Frame (LSB First, 31-bit) ========== -\t; Frame: start@0, data[30:0]@[31:1], stop@32 -\t; Total: 33 bits = 5 bytes -\tmov TEMP_REG1, ${dataRegLo} ; 31 bits of data (bits [30:0]) -\tldi TEMP_REG2, 0 ; Clear TEMP_REG2 for stop bit -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift left by 1 for start bit -`; - if (startBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set start bit at position 0 -`; - } - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG2, TEMP_REG2, 0 ; Set stop bit at position 32 (bit 0 of TEMP_REG2) -`; - } - macroBody += ` -\t; ========== Load Initial 4 Bytes to FIFO (LSB First) ========== -\tmov r30.b0, TEMP_REG1.b0 -\tmov r30.b0, TEMP_REG1.b1 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b3 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -\t; ========== Load Byte 5 via Continuous Polling ========== -wait_fifo_for_byte5?: -\tmov ${dataRegLo}, r31 -\tlsr ${dataRegLo}, ${dataRegLo}, 2 -\tand ${dataRegLo}, ${dataRegLo}, 0x7 -\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 ; Wait until FIFO <= 2 -\tmov r30.b0, TEMP_REG2.b0 - -`; - } else if (bitSize === 32) { - // 32 bits LSB: frame = 34 bits - // Data bits [31:0] -> start@0, data[30:0]@[31:1], data[31]@32, stop@33 - // IMPORTANT: Set unused bits [7:2] to 1 to prevent false start bit detection after stop - macroBody += `\t; ========== Construct Frame (LSB First, 32-bit) ========== -\t; Frame: start@0, data[30:0]@[31:1], data[31]@32, stop@33 -\t; Total: 34 bits = 5 bytes -\t; Set unused bits [7:2] to 1 to prevent false start bit detection after stop -\tmov TEMP_REG1, ${dataRegLo} ; 32 bits of data -\t; Save bit 31 before shifting, and set unused bits [7:2] to 1 -\tlsr TEMP_REG2, TEMP_REG1, 31 ; Save bit 31 into bit 0 of TEMP_REG2 -\tor TEMP_REG2, TEMP_REG2, 0xFC ; Set bits [7:2] = 1 to prevent false start detection -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift lower 31 bits left by 1 -`; - if (startBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set start bit at position 0 -`; - } - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG2, TEMP_REG2, 1 ; Set stop bit at position 33 (bit 1 of TEMP_REG2) -`; - } else { - macroBody += `\tclr TEMP_REG2, TEMP_REG2, 1 ; Clear stop bit at position 33 (bit 1 of TEMP_REG2) -`; - } - macroBody += ` -\t; ========== Load Initial 4 Bytes to FIFO (LSB First) ========== -\tmov r30.b0, TEMP_REG1.b0 -\tmov r30.b0, TEMP_REG1.b1 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b3 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -\t; ========== Load Byte 5 via Continuous Polling ========== -wait_fifo_for_byte5?: -\tmov ${dataRegLo}, r31 -\tlsr ${dataRegLo}, ${dataRegLo}, 2 -\tand ${dataRegLo}, ${dataRegLo}, 0x7 -\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 ; Wait until FIFO <= 2 -\tmov r30.b0, TEMP_REG2.b0 - -`; - } - } else { - // MSB mode - if (bitSize === 30) { - // 30 bits MSB: frame = 32 bits, all fits in TEMP_REG1 - // Data bits [29:0] -> shifted to [30:1], start at bit 31, stop at bit 0 - macroBody += `\t; ========== Construct Frame (MSB First, 30-bit) ========== -\t; Frame: start@31, data[29:0]@[30:1], stop@0 -\t; Total: 32 bits = 4 bytes -\tmov TEMP_REG1, ${dataRegLo} ; 30 bits of data (bits [29:0]) -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift left by 1 for stop bit at position 0 -`; - if (startBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 31 ; Set start bit at position 31 -`; - } - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set stop bit at position 0 -`; - } - // Note: For stop bit = 0, bit 0 is already 0 after left shift, no need to clear - macroBody += ` -\t; ========== Load 4 Bytes to FIFO (MSB First) ========== -\tmov r30.b0, TEMP_REG1.b3 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b1 -\tmov r30.b0, TEMP_REG1.b0 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -`; - } else if (bitSize === 31) { - // 31 bits MSB: frame = 33 bits - // TEMP_REG2.b0 bit 0 = start, TEMP_REG1 = 31 data bits + stop at bit 0 - // Load order: TEMP_REG2.b0, then TEMP_REG1.b3, b2, b1, b0 - // IMPORTANT: Set unused bits [7:1] to 1 to prevent false start bit detection (only needed when start=0) - macroBody += `\t; ========== Construct Frame (MSB First, 31-bit) ========== -\t; Frame: start@32 (TEMP_REG2 bit 0), data[30:0]@[31:1], stop@0 -\t; Total: 33 bits = 5 bytes -\t; Load order: TEMP_REG2.b0, TEMP_REG1.b3, b2, b1, b0 -`; - if (startBitPol === "0") { - macroBody += `\t; Set unused bits [7:1] to 1 to prevent false start bit detection when start=0 -\tldi TEMP_REG2, 0xFE ; Set bits [7:1] = 1, bit 0 = 0 (start bit) -`; - } else { - macroBody += `\tldi TEMP_REG2, 0 -\tset TEMP_REG2, TEMP_REG2, 0 ; Set start bit at bit 0 of TEMP_REG2 -`; - } - macroBody += `\tmov TEMP_REG1, ${dataRegLo} ; 31 bits of data (bits [30:0]) -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift left by 1 for stop bit at position 0 -`; - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set stop bit at position 0 -`; - } - macroBody += ` -\t; ========== Load Initial 4 Bytes to FIFO (MSB First) ========== -\t; Load order: TEMP_REG2.b0 (start bit), TEMP_REG1.b3, b2, b1 -\tmov r30.b0, TEMP_REG2.b0 -\tmov r30.b0, TEMP_REG1.b3 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b1 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -\t; ========== Load Byte 5 via Continuous Polling ========== -wait_fifo_for_byte5?: -\tmov ${dataRegLo}, r31 -\tlsr ${dataRegLo}, ${dataRegLo}, 2 -\tand ${dataRegLo}, ${dataRegLo}, 0x7 -\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 ; Wait until FIFO <= 2 -\tmov r30.b0, TEMP_REG1.b0 ; Load byte 5 directly from TEMP_REG1.b0 - -`; - } else if (bitSize === 32) { - // 32 bits MSB: frame = 34 bits - EXACTLY MATCHING main.asm working code - // TEMP_REG2.b0: start@bit1, data[31]@bit0 (like r17 in main.asm) - // TEMP_REG1: data[30:0]@[31:1], stop@bit0 (like r16 in main.asm) - // Load order: TEMP_REG2.b0, TEMP_REG1.b3, b2, b1, b0 - // IMPORTANT: Set unused bits [7:2] to 1 to prevent false start bit detection (only needed when start=0) - macroBody += `\t; ========== Construct Frame (MSB First, 32-bit) - SAME AS WORKING main.asm ========== -\t; Frame: start@33 (TEMP_REG2 bit 1), data[31]@32 (TEMP_REG2 bit 0), data[30:0]@[31:1], stop@0 -\t; Total: 34 bits = 5 bytes -\t; Load order: TEMP_REG2.b0, TEMP_REG1.b3, b2, b1, b0 -\t; TEMP_REG2 = r17 in main.asm, TEMP_REG1 = r16 in main.asm, dataRegLo = r18 in main.asm -`; - if (startBitPol === "0") { - macroBody += `\t; Set unused bits [7:2] to 1 to prevent false start bit detection when start=0 -\tldi TEMP_REG2, 0xFC ; Set bits [7:2] = 1, bits [1:0] = 0 -`; - } else { - macroBody += `\tldi TEMP_REG2, 0 -\tset TEMP_REG2, TEMP_REG2, 1 ; Set start bit at bit 1 of TEMP_REG2 -`; - } - macroBody += `\tmov TEMP_REG1, ${dataRegLo} ; 32 bits of data (TEMP_REG1 = r16) -\t; Save bit 31 of data into bit 0 of TEMP_REG2 -\tlsr ${dataRegLo}, ${dataRegLo}, 31 ; Get bit 31 (dataRegLo used as temp, like r18) -\tor TEMP_REG2, TEMP_REG2, ${dataRegLo} ; OR into bit 0 of TEMP_REG2 -\tlsl TEMP_REG1, TEMP_REG1, 1 ; Shift remaining 31 bits left by 1 for stop bit -`; - if (stopBitPol === "1") { - macroBody += `\tset TEMP_REG1, TEMP_REG1, 0 ; Set stop bit at position 0 -`; - } - macroBody += ` -\t; ========== Load Initial 4 Bytes to FIFO (MSB First) ========== -\t; Load order: TEMP_REG2.b0 (start + MSB), TEMP_REG1.b3, b2, b1 -\tmov r30.b0, TEMP_REG2.b0 -\tmov r30.b0, TEMP_REG1.b3 -\tmov r30.b0, TEMP_REG1.b2 -\tmov r30.b0, TEMP_REG1.b1 - -\t; ========== Start Transmission ========== -\tset r31, r31, 18 - -\t; ========== Load Byte 5 via Continuous Polling ========== -wait_fifo_for_byte5?: -\tmov ${dataRegLo}, r31 -\tlsr ${dataRegLo}, ${dataRegLo}, 2 -\tand ${dataRegLo}, ${dataRegLo}, 0x7 -\tqblt wait_fifo_for_byte5?, ${dataRegLo}, 2 ; Wait until FIFO <= 2 -\tmov r30.b0, TEMP_REG1.b0 ; Load byte 5 directly from TEMP_REG1.b0 - -`; - } - } - - // Wait for FIFO empty and TX complete (for 31 and 32 bit cases) - if (bitSize >= 31) { - macroBody += `\t; ========== Wait for FIFO Empty ========== -wait_fifo_empty?: -\tmov TEMP_REG1, r31 -\tlsr TEMP_REG1, TEMP_REG1, 2 -\tand TEMP_REG1, TEMP_REG1, 0x7 -\tqbne wait_fifo_empty?, TEMP_REG1, 0 - -`; - } - - macroBody += `\t; ========== Wait for TX Complete ========== -\t.if ${channel} == 0 -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 5 -\t.elseif ${channel} == 1 -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 13 -\t.else -wait_tx_done?: -\tqbbs wait_tx_done?, r31, 21 -\t.endif -`; - - if (pruInstructionMacro == "") { - macroBody += "\n .endm"; - } - - return macroBody; -} - -function getAIContext() { - return getLongDescription() + ` - -## How to Configure (For AI/Scripting) - -This section describes how to programmatically configure the UART TX block in a .syscfg file. - -### Adding a UART TX Instance - -\`\`\`javascript -const uart_tx = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx", {}, false); -const uart_tx1 = uart_tx.addInstance(); -\`\`\` - -### Configuration Parameters - -| Parameter | Type | Valid Values | Default | Description | -|-----------|------|--------------|---------|-------------| -| pruSelect | Integer | 0, 1 | Auto-detected | PRU Selection (0=PRU0, 1=PRU1) - auto-detected from system context (read-only) | -| channel | Integer | 0, 1, 2 | 0 | ENDAT channel for UART TX | -| clockSource | Integer | 0, 1 | 0 | Clock source (0=192MHz UART_CLK, 1=200MHz CORE_CLK) | -| baudRate | Integer | MHz value | 12 | Baud rate in MHz. Clock source must be divisible by baudRate | -| startBitPolarity | Integer | 0, 1 | 1 | Start bit polarity (0=Low, 1=High) | -| stopBitPolarity | Integer | 0, 1 | 0 | Stop bit polarity (0=Low, 1=High) | -| bitSwap | Boolean | true, false | true | Enable LSB-first transmission (true) or MSB-first (false) | -| dataBits | Integer | 1-62 | 8 | Number of data bits to transmit (NOT including start/stop) | - -### Valid Baud Rates -**With 192 MHz clock source (default):** -192 / baudRate must be an integer. Valid baud rates include: -- 192 MHz (clockDivider = 0) -- 96 MHz (clockDivider = 1) -- 64 MHz (clockDivider = 2) -- 48 MHz (clockDivider = 3) -- 32 MHz (clockDivider = 5) -- 24 MHz (clockDivider = 7) -- 16 MHz (clockDivider = 11) -- 12 MHz (clockDivider = 15) -- 8 MHz (clockDivider = 23) -- 6 MHz (clockDivider = 31) -- 4 MHz (clockDivider = 47) -- 3 MHz (clockDivider = 63) -- 2 MHz (clockDivider = 95) -- 1 MHz (clockDivider = 191) - -**With 200 MHz clock source:** -200 / baudRate must be an integer. Valid baud rates include: -- 200 MHz (clockDivider = 0) -- 100 MHz (clockDivider = 1) -- 50 MHz (clockDivider = 3) -- 40 MHz (clockDivider = 4) -- 25 MHz (clockDivider = 7) -- 20 MHz (clockDivider = 9) -- 10 MHz (clockDivider = 19) -- 8 MHz (clockDivider = 24) -- 5 MHz (clockDivider = 39) -- 4 MHz (clockDivider = 49) -- 2 MHz (clockDivider = 99) -- 1 MHz (clockDivider = 199) - -### Example Configurations - -**Standard 8-bit UART at 12 MHz (192 MHz clock):** -\`\`\`javascript -uart_tx1.$name = "UART_TX_0"; -uart_tx1.pruSelect = 0; -uart_tx1.channel = 0; -uart_tx1.clockSource = 0; // 192 MHz (UART_CLK) -uart_tx1.baudRate = 12; // 12 MHz baud rate -uart_tx1.dataBits = 8; -uart_tx1.bitSwap = true; // LSB first (standard UART) -uart_tx1.startBitPolarity = 0; // Standard UART start bit (low) -uart_tx1.stopBitPolarity = 1; // Standard UART stop bit (high) -\`\`\` - -**8-bit UART at 10 MHz using 200 MHz clock:** -\`\`\`javascript -uart_tx1.$name = "UART_TX_200MHz"; -uart_tx1.pruSelect = 0; -uart_tx1.channel = 0; -uart_tx1.clockSource = 1; // 200 MHz (CORE_CLK) -uart_tx1.baudRate = 10; // 10 MHz baud rate (200/10 = 20, divider = 19) -uart_tx1.dataBits = 8; -uart_tx1.bitSwap = true; -uart_tx1.startBitPolarity = 0; -uart_tx1.stopBitPolarity = 1; -\`\`\` - -**Extended 32-bit transmission on PRU1, Channel 2:** -\`\`\`javascript -uart_tx1.$name = "UART_TX_Extended"; -uart_tx1.pruSelect = 1; -uart_tx1.channel = 2; -uart_tx1.clockSource = 0; // 192 MHz (UART_CLK) -uart_tx1.baudRate = 24; // 24 MHz baud rate -uart_tx1.dataBits = 32; // 32 data bits (uses continuous mode) -uart_tx1.bitSwap = false; // MSB first -uart_tx1.startBitPolarity = 1; -uart_tx1.stopBitPolarity = 0; -\`\`\` - -### Connecting to Other Blocks - -\`\`\`javascript -// Connect data source to UART TX input -scripting.connect(load_constant1, "output1", uart_tx1, "input1"); - -// For dataBits > 32, also connect upper 32 bits -scripting.connect(load_constant2, "output1", uart_tx1, "input2"); - -// Connect control flow -scripting.connect(prev_block, "next", uart_tx1, "prev"); -\`\`\` - -### Important Notes for Extended Mode (dataBits > 32) - -When transmitting more than 32 data bits, you need **two Load Constant blocks** connected to the UART TX: -- **input1**: Lower 32 bits of data (from first Load Constant block) -- **input2**: Upper bits of data (from second Load Constant block) - -**Example for 40-bit transmission:** -\`\`\`javascript -// Add two Load Constant blocks for 40-bit data -const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); -const load_constant1 = load_constant_block.addInstance(); -const load_constant2 = load_constant_block.addInstance(); - -load_constant1.$name = "Load_Constant_Lower"; -load_constant1.constant1 = 0xAABBCCDD; // Lower 32 bits - -load_constant2.$name = "Load_Constant_Upper"; -load_constant2.constant1 = 0xFF; // Upper 8 bits (bits 32-39) - -// UART TX configured for 40 data bits -uart_tx1.$name = "UART_TX_40bit"; -uart_tx1.dataBits = 40; - -// Connect both inputs -scripting.connect(load_constant1, "output1", uart_tx1, "input1"); -scripting.connect(load_constant2, "output1", uart_tx1, "input2"); -\`\`\` -`; -} - -function getLongDescription() { - return ` -## UART TX (Hardware-Accelerated via ENDAT) - -### Purpose -Configures the PRU-ICSS ENDAT peripheral hardware for UART transmission at configurable baud rates. Supports two modes: -- **Single-shot mode** (1-29 data bits): Frame size ≤ 31 bits, uses standard FIFO pre-load -- **Continuous mode** (30-62 data bits): Frame size > 31 bits, uses continuous FIFO loading with polling - -### How It Works -1. **Peripheral Reset Sequence**: Ensures clean peripheral state before transmission - - Clears all RX enables (R30 bits 24, 25, 26) to prevent interference - - Triggers Global Reinit (R31 bit 19) to clear FIFO and state machines - - **Polls for busy flag to clear** before proceeding (CH0=bit5, CH1=bit13, CH2=bit21) - - This is critical for consistent behavior when reloading firmware without full system reset, as the ENDAT peripheral state persists across PRU core resets -2. **Peripheral Mode**: Sets GPCFG to enable peripheral interface mode for selected PRU -3. **TX Clock Configuration**: Configures TX clock divider - - TX frequency = Core_Clock / (divider + 1) - - Example: 192 MHz / (15+1) = 12 MHz baud rate -4. **Frame Configuration**: - - Single-shot: Sets frame size to (dataBits + 2) - - Continuous: Sets frame size to 0 (continuous transmission) -5. **Clock Mode**: Sets mode 3 (stop clock high after TX completes) -6. **Channel Selection**: Selects TX channel via R30[17:16] -7. **Disable RX**: Disables RX on the selected channel to prevent interference -8. **Frame Construction**: Constructs frame from input data with start and stop bits -9. **Global Reinit**: Triggers another reinit after configuration - - Ensures clean state before FIFO loading - - Followed by delay (20 NOP cycles) to allow reinit to complete -10. **FIFO Loading**: - - Single-shot: Pre-loads 1-4 bytes - - Continuous: Pre-loads 4 bytes, then polls FIFO status to load remaining 1-4 bytes -11. **Transmission Start**: Triggers transmission by setting R31 bit 18 -12. **Wait for Completion**: - - Continuous: Waits for FIFO empty, then waits for busy flag to clear - - Single-shot: Waits for busy flag to clear - -### Configuration Parameters - -**Input Ports** -- **input1**: Lower 32 bits of data to transmit -- **input2**: Upper 32 bits of data (used when dataBits > 32) - -**PRU Selection** (PRU0 or PRU1) -- Automatically detected from system context (read-only) -- Determines peripheral base addresses -- The PRU number is extracted from the project configuration - -**Channel Selection** (0, 1, or 2) -- Selects which ENDAT channel to use for UART TX -- Default: 0 - -**Clock Source** (192 MHz or 200 MHz) -- TX clock source selection -- 192 MHz: ICSSGn_UART_CLK (default) - tested up to 32 MHz baud rate -- 200 MHz: ICSSGn_CORE_CLK - tested up to 20 MHz baud rate -- Configured via PRU0_ED_TX_CLK_SEL bit in ICSSG_PRU0_ED_TX_CFG_REG -- Default: 192 MHz - -**Baud Rate (MHz)** -- Desired UART baud rate in MHz -- Selected clock source must be divisible by baudRate -- Clock divider is automatically calculated: (clockSource / baudRate) - 1 -- Example with 192 MHz: 12 MHz baud → clockDivider = (192 / 12) - 1 = 15 -- Example with 200 MHz: 10 MHz baud → clockDivider = (200 / 10) - 1 = 19 -- Default: 12 MHz - -**Start Bit Polarity** (0 or 1) -- Polarity of start bit -- 0 = Low/Space, 1 = High/Mark -- Default: 1 - -**Stop Bit Polarity** (0 or 1) -- Polarity of stop bit (independent of start bit) -- 0 = Low/Space, 1 = High/Mark -- Default: 0 - -**Start/Stop Bit Combinations** -| Start | Stop | Macro Suffix | -|-------|------|--------------| -| 0 | 1 | _start0 (default) | -| 1 | 0 | _start1 (default) | -| 0 | 0 | _start0_stop0 | -| 1 | 1 | _start1_stop1 | - -**Role of Start and Stop Bits** -- **Start Bit**: Signals the beginning of a frame. The receiver uses this transition to synchronize and begin sampling. The polarity determines the edge direction (0=falling from high idle, 1=rising from low idle). -- **Stop Bit**: Marks the end of the frame and returns the line to a known state before the next frame. Allows the receiver to validate frame integrity (framing error if stop bit doesn't match expected polarity). - -**Bit Swap (LSB First)** -- Enable LSB-first transmission -- Default: true (LSB first) - -**Data Bits** (1-62) -- **IMPORTANT**: This is ONLY the number of DATA bits to transmit, NOT including start/stop bits -- The block automatically adds the start bit and stop bit to construct the complete frame -- Frame size = data bits + 2 (1 start bit + 1 stop bit) - calculated automatically -- Example: To transmit 8 data bits, set dataBits = 8 (block creates 10-bit frame: start + 8 data + stop) -- Example: To transmit 52 data bits, set dataBits = 52 (block creates 54-bit frame: start + 52 data + stop) -- 1-29: Single-shot mode -- 30-62: Continuous mode -- Default: 8 - -### Frame Size and Mode Selection - -| Data Bits | Frame Size | Mode | Total Bytes | Initial Load | Continuous Load | -|-----------|------------|-------------|-------------|--------------|-----------------| -| 1-6 | 3-8 | Single-shot | 1 | 1 | 0 | -| 7-14 | 9-16 | Single-shot | 2 | 2 | 0 | -| 15-22 | 17-24 | Single-shot | 3 | 3 | 0 | -| 23-29 | 25-31 | Single-shot | 4 | 4 | 0 | -| 30-37 | 32-39 | Continuous | 5 | 4 | 1 | -| 38-45 | 40-47 | Continuous | 6 | 4 | 2 | -| 46-53 | 48-55 | Continuous | 7 | 4 | 3 | -| 54-62 | 56-64 | Continuous | 8 | 4 | 4 | - -### Continuous Mode Details - -In continuous mode (dataBits > 29): -- Frame size register is set to 0 (continuous transmission) -- First 4 bytes are pre-loaded to FIFO -- Transmission is started -- Remaining bytes are loaded by polling tx_fifo_sts (R31[4:2] for CH0) -- When FIFO level ≤ 2, next byte is loaded -- After all bytes loaded, wait for FIFO empty then TX complete - -### Supported Data Sizes and Internal Modes - -| Data Bits | Frame Size | Internal Mode | Macro Type | -|-----------|------------|---------------|------------| -| 1-29 | 3-31 | Single-shot | Byte-count based | -| 30 | 32 | Continuous | Bit-specific (30bit) | -| 31 | 33 | Continuous | Bit-specific (31bit) | -| 32 | 34 | Continuous | Bit-specific (32bit) | -| 33-62 | 35-64 | Continuous | Byte-count based | - -**Note:** The block automatically handles frame construction, adding start and stop bits, and manages FIFO loading based on the data size. - -### Register Usage -- **TEMP_REG1 (R28)**: First 32 bits of constructed frame -- **TEMP_REG2 (R29)**: Second 32 bits of constructed frame (continuous mode) -- **R0**: Used for FIFO status polling in continuous mode - ---- -`; -} - -exports = { - displayName: "PRU UART TX", - defaultInstanceName: `PRU${PRU_USED}_UART_TX_`, - longDescription: getLongDescription(), - getAIContext: getAIContext, - uiView: "graph", - requiredIncludes: [ - /*TODO : review on how to add include files for the modules*/ - // for a workaround defined the registers in the UART block itself - ], - templates: { - "/pru_blocks/common/pru_syscfg.asm.xdt": null - }, - config: [ - { - name: "$shape", - hidden: true, - default: "square", - }, - { - name: "$topLabel", - hidden: true, - default: "", - }, - // ========== User Configuration Parameters ========== - { - name: "pruSelect", - displayName: "PRU Selection", - description: "Auto-detected from system context (read-only)", - default: getPruNumberFromContext(), - readOnly: true, - options: [ - { name: 0, displayName: "PRU0" }, - { name: 1, displayName: "PRU1" } - ] - }, - { - name: "channel", - displayName: "Channel Selection", - description: "ENDAT channel to use for UART TX (0, 1, or 2)", - default: 0, - options: [ - { name: 0, displayName: "Channel 0" }, - { name: 1, displayName: "Channel 1" }, - { name: 2, displayName: "Channel 2" } - ] - }, - { - name: "clockSource", - displayName: "Clock Source", - description: "TX clock source selection. 192 MHz (ICSSGn_UART_CLK) or 200 MHz (ICSSGn_CORE_CLK)", - default: 0, - options: [ - { name: 0, displayName: "192 MHz (UART_CLK)" }, - { name: 1, displayName: "200 MHz (CORE_CLK)" } - ] - }, - { - name: "baudRate", - displayName: "Baud Rate (MHz)", - description: "Desired UART baud rate in MHz. Clock must be divisible by baudRate.", - default: 12, - displayFormat: "dec" - }, - { - name: "clockDivider", - displayName: "Clock Divider (Calculated)", - description: "Calculated clock divider = (clockSource / baudRate) - 1", - default: 15, - readOnly: true, - getValue: (inst) => { - // Get clock frequency based on clock source selection - const uartClkMHz = inst.clockSource === 0 ? 192 : 200; - if (inst.baudRate === 0 || uartClkMHz % inst.baudRate !== 0) { - return 0; // Invalid, validation will catch this - } - return (uartClkMHz / inst.baudRate) - 1; - } - }, - { - name: "startBitPolarity", - displayName: "Start Bit Polarity", - description: "Polarity of start bit. Standard UART: 0 (line goes low to indicate start)", - default: 1, - options: [ - { name: 0, displayName: "0 (Low/Space)" }, - { name: 1, displayName: "1 (High/Mark)" } - ] - }, - { - name: "stopBitPolarity", - displayName: "Stop Bit Polarity", - description: "Polarity of stop bit. Standard UART: 1 (line returns high after data)", - default: 0, - options: [ - { name: 0, displayName: "0 (Low/Space)" }, - { name: 1, displayName: "1 (High/Mark)" } - ] - }, - { - name: "bitSwap", - displayName: "Bit Swap (LSB First)", - description: "Enable LSB-first transmission (standard UART). When disabled, MSB is transmitted first", - default: true - }, - { - name: "dataBits", - displayName: "Data Bits", - description: "Number of data bits to transmit (1-62). 1-29: single-shot mode, 30-62: continuous mode", - default: 8, - range: [1, 62] - }, - // ========== Calculated/Hidden Configuration ========== - { - name: "txMode", - displayName: "TX Mode", - description: "Transmission mode based on data bits", - hidden: false, - getValue: (inst) => { - return inst.dataBits <= 29 ? "Single-shot" : "Continuous"; - }, - default: "Single-shot" - }, - { - name: "constant1", - hidden: true, - default: "0, 15, 0, 8", - getValue: (inst) => { - // Build the macro parameters string - // Parameters: channel, clockDiv, txClkSel, dataBits (dataBits omitted for 30/31/32 bit specific macros) - // dataRegLo and dataRegHi come from connected input blocks - const channel = inst.channel; - const txClkSel = inst.clockSource; // 0=192MHz, 1=200MHz - - // Calculate clock divider from baud rate using selected clock source - const uartClkMHz = inst.clockSource === 0 ? 192 : 200; - const clockDiv = (inst.baudRate === 0 || uartClkMHz % inst.baudRate !== 0) - ? 0 - : (uartClkMHz / inst.baudRate) - 1; - - const dataBits = inst.dataBits; - - // For 30, 31, 32 bit specific macros, dataBits is encoded in macro name - if (dataBits >= 30 && dataBits <= 32) { - return `${channel}, ${clockDiv}, ${txClkSel}`; - } - return `${channel}, ${clockDiv}, ${txClkSel}, ${dataBits}`; - }, - }, - { - name: "opCode", - displayName: "m_uart_tx", - default: "m_uart_tx", - hidden: true, - getValue: (inst) => { - // Generate macro name based on data bits, PRU, bit order, start bit and stop bit polarity - // For 30, 31, 32 bits: use specific bit-size macros - // For others: use byte-count based macros - const pruNum = inst.pruSelect; - const bitOrder = inst.bitSwap ? "lsb" : "msb"; - const startBit = inst.startBitPolarity; - const stopBit = inst.stopBitPolarity; - - // Determine if we need explicit stop bit suffix - // Default behavior: start=0->stop=1, start=1->stop=0 - // Only add _stop suffix for non-default cases (start=stop) - const needsStopSuffix = (startBit === stopBit); - const stopSuffix = needsStopSuffix ? `_stop${stopBit}` : ""; - - if (inst.dataBits >= 30 && inst.dataBits <= 32) { - // Use specific bit-size macro for 30, 31, 32 bits - return `m_uart_tx_${inst.dataBits}bit_pru${pruNum}_${bitOrder}_start${startBit}${stopSuffix}`; - } else { - // Use byte-count based macro for other sizes - const frameSize = inst.dataBits + 2; - const numFifoBytes = Math.floor((frameSize + 7) / 8); - return `m_uart_tx_${numFifoBytes}byte_pru${pruNum}_${bitOrder}_start${startBit}${stopSuffix}`; - } - }, - }, - { - name: "outputReg", - default: "None", - hidden: true - }, - { - name: "numOfInputPorts", - default: 1, - hidden: true, - getValue: (inst) => { - // Single-shot (1-32 bits): only needs input1 - // Continuous (33-62 bits): needs input1 and input2 - return inst.dataBits > 32 ? 2 : 1; - } - }, - { - name: "numOfOutputPorts", - default: 0, - hidden: true - }, - { - name: "numOfConstants", - default: 1, - hidden: true - }, - { - name: "uartBitPeriod", - displayName: "UART Bit Period", - description: "Calculated period per bit based on baud rate", - hidden: false, - getValue: (inst) => { - // Bit period = 1 / baudRate (in MHz) = 1000 / baudRate (in ns) - const bitPeriodNs = 1000 / inst.baudRate; - - if (bitPeriodNs >= 1000) { - return (bitPeriodNs / 1000).toFixed(2) + " us"; - } else { - return bitPeriodNs.toFixed(2) + " ns"; - } - }, - default: "N/A" - }, - { - name: "totalBytes", - displayName: "Total Bytes", - description: "Total bytes to transmit including start and stop bits", - hidden: false, - getValue: (inst) => { - const frameSize = inst.dataBits + 2; - return Math.floor((frameSize + 7) / 8); - }, - default: 2 - }, - { - name: "noOfCycles", - displayName: "Number Of Cycles", - getValue: (inst) => { - const frameSize = inst.dataBits + 2; - const numFifoBytes = Math.floor((frameSize + 7) / 8); - - if (numFifoBytes <= 4) { - // Single-shot mode: ~25 base + FIFO loads - return 25 + numFifoBytes; - } else { - // Continuous mode: ~25 base + 4 initial + polling overhead + remaining bytes - // Polling overhead: ~5 cycles per byte (mov, lsr, and, qblt, mov) - const continuousBytes = numFifoBytes - 4; - return 25 + 4 + (continuousBytes * 6) + 5; // +5 for final waits - } - }, - default: 27 - }, - { - name: "input1Size", - hidden: true, - default: 4, - getValue: (inst) => { - // For dataBits 1-8: 1 byte, 9-16: 2 bytes, 17-24: 3->4 bytes, 25+: 4 bytes - const dataBits = inst.dataBits; - if (dataBits <= 8) return 1; - if (dataBits <= 16) return 2; - return 4; // 17+ bits needs 4 bytes (3 bytes rounds to 4) - } - }, - { - name: "input2Size", - hidden: true, - default: 0, - getValue: (inst) => { - // Only need input2 for dataBits > 32 (continuous mode with two registers) - return inst.dataBits > 32 ? 4 : 0; - } - } - ], - ports: (inst) => { - let ports = []; - // Two input ports: input1 (lower 32 bits), input2 (upper 32 bits) - for (let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input" + iterator.toString(), type: "input" }); - } - ports.push({ name: "prev", type: "PREV" }); - ports.push({ name: "next", type: "NEXT" }); - return ports; - }, - validate, - moduleStatic: { - modules: function(inst) { - return [{ - name: "pru_register_allocator_validator", - moduleName: "/pru_blocks/common/pru_blocks_static_module" - }] - }, - }, - getMacro -} \ No newline at end of file diff --git a/.metadata/sysconfig/.meta/pru_blocks/utils/access_look_up_table.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/utils/access_look_up_table.syscfg.js index 459f94c..8e0e7b6 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/utils/access_look_up_table.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/utils/access_look_up_table.syscfg.js @@ -323,12 +323,12 @@ exports = { ports: (inst) => { let ports = []; - ports.push({ name: "input1", displayName: "index", type: "input" }) + ports.push({ name: "input1", displayName: "index", type: "input32" }) ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT" }) - ports.push({ name: "output1", displayName: "dout", type: "output" }) + ports.push({ name: "output1", displayName: "dout", type: "output32" }) return ports }, diff --git a/.metadata/sysconfig/.meta/pru_blocks/utils/data_splitter.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/utils/data_splitter.syscfg.js new file mode 100644 index 0000000..1a6787d --- /dev/null +++ b/.metadata/sysconfig/.meta/pru_blocks/utils/data_splitter.syscfg.js @@ -0,0 +1,148 @@ +/** + * Data Splitter Block + * + * Takes a 64-bit input (two consecutive registers) and outputs either + * the lower 32 bits or upper 32 bits as a single 32-bit output. + */ + +function getMacro(pruInstructionMacro, opCode) { + const isUpper = opCode.includes("_upper"); + + if (pruInstructionMacro === "") { + let macroBody = opCode + "\t.macro dataOut, dataRegLo, dataRegHi\n"; + macroBody += `\t; Data Splitter — extract ${isUpper ? "upper" : "lower"} 32 bits from 64-bit input +\t; dataOut - Destination register (32-bit output) +\t; dataRegLo - Lower 32 bits of 64-bit input +\t; dataRegHi - Upper 32 bits of 64-bit input +\tmov dataOut, ${isUpper ? "dataRegHi" : "dataRegLo"} +`; + macroBody += " .endm"; + return macroBody; + } + + return pruInstructionMacro; +} + +function getLongDescription() { + return ` +## Data Splitter Block + +### Purpose +Extracts the lower or upper 32 bits from a 64-bit input (two consecutive registers). +Connect the output of a 64-bit block (e.g. UART RX Op in extended mode, Memory Access +with dataSize > 4) to this block's input, and select which half you need. + +### Generated Assembly +**Lower 32 bits:** +\`\`\`assembly +MOV R_out, dataRegLo ; copy lower register +\`\`\` + +**Upper 32 bits:** +\`\`\`assembly +MOV R_out, dataRegHi ; copy upper register +\`\`\` + +### Ports +- **in64**: 64-bit data input (two consecutive registers) +- **out32**: 32-bit data output (selected half) + +### Configuration +- **Split Select**: Choose \`lower\` (bits 31:0) or \`upper\` (bits 63:32) +`; +} + +exports = { + displayName: "Data Splitter", + defaultInstanceName: "Data_Splitter_", + longDescription: getLongDescription(), + uiView: "graph", + templates: { + "/pru_blocks/common/pru_syscfg.asm.xdt": null + }, + config: [ + { + name: "$shape", + hidden: true, + default: "square", + }, + { + name: "$topLabel", + hidden: true, + default: "", + }, + { + name: "splitSelect", + displayName: "Split Select", + description: "Select which 32-bit half of the 64-bit input to output", + default: "lower", + options: [ + { name: "lower", displayName: "Lower 32 bits (bits 31:0)" }, + { name: "upper", displayName: "Upper 32 bits (bits 63:32)" }, + ] + }, + // ===== HIDDEN CODE GEN FIELDS ===== + { + name: "opCode", + displayName: "m_data_splitter", + default: "m_data_splitter_lower", + hidden: true, + getValue: (inst) => { + return inst.splitSelect === "upper" + ? "m_data_splitter_upper" + : "m_data_splitter_lower"; + } + }, + { + name: "outputReg", + default: "", + hidden: true + }, + { + name: "numOfInputPorts", + default: 1, + hidden: true + }, + { + name: "numOfOutputPorts", + default: 1, + hidden: true + }, + { + name: "numOfConstants", + default: 0, + hidden: true + }, + { + name: "input1Size", + default: 8, + hidden: true + }, + { + name: "output1Size", + default: 4, + hidden: true + }, + { + name: "noOfCycles", + displayName: "Number Of Cycles", + default: 1, + getValue: (_inst) => 1 + }, + ], + ports: (_inst) => [ + { name: "input1", type: "input64" }, + { name: "output1", type: "output32" }, + { name: "prev", type: "PREV" }, + { name: "next", type: "NEXT" }, + ], + getMacro, + moduleStatic: { + modules: function(inst) { + return [{ + name: "pru_register_allocator_validator", + moduleName: "/pru_blocks/common/pru_blocks_static_module" + }]; + }, + }, +} diff --git a/.metadata/sysconfig/.meta/pru_blocks/utils/delay_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/utils/delay_block.syscfg.js index dc046ca..864b2ad 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/utils/delay_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/utils/delay_block.syscfg.js @@ -280,11 +280,11 @@ exports = { let ports = []; for(let iterator = 1; iterator <= inst["numOfInputPorts"]; iterator++) { - ports.push({ name: "input"+iterator.toString(), type: "input" }) + ports.push({ name: "input"+iterator.toString(), type: "input32" }) } for(let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output"+iterator.toString(), type: "output"}) + ports.push({ name: "output"+iterator.toString(), type: "output32"}) } ports.push({ name: "prev", type: "PREV" }) ports.push({ name: "next", type: "NEXT"}) diff --git a/.metadata/sysconfig/.meta/pru_blocks/utils/look_up_table.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/utils/look_up_table.syscfg.js index b72ae3e..c040dfe 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/utils/look_up_table.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/utils/look_up_table.syscfg.js @@ -2,8 +2,7 @@ let copyCmd = "cp"; let copyArgs = ["$browsedFile", "$comFile"]; if (system.getOS() == "win") { - copyCmd = "cmd.exe"; - copyArgs = ["/c", "copy", "$browsedFile", "$comFile"]; + copyCmd = "copy"; } function getLookUptable(instance) { @@ -824,7 +823,7 @@ Then click "Browse For JSON File" button above to select it.`; ports.push({ name: "next", type: "NEXT" }) for (let iterator = 1; iterator <= inst["numOfOutputPorts"]; iterator++) { - ports.push({ name: "output" + iterator.toString(), type: "output" }) + ports.push({ name: "output" + iterator.toString(), type: "output32" }) } return ports diff --git a/.metadata/sysconfig/.meta/pru_blocks/utils/memory_access_block.syscfg.js b/.metadata/sysconfig/.meta/pru_blocks/utils/memory_access_block.syscfg.js index 27032d9..a556b9f 100644 --- a/.metadata/sysconfig/.meta/pru_blocks/utils/memory_access_block.syscfg.js +++ b/.metadata/sysconfig/.meta/pru_blocks/utils/memory_access_block.syscfg.js @@ -72,8 +72,8 @@ function validate(inst, report) { } // Validate data size - if (inst["dataSize"] < 1 || inst["dataSize"] > 4) { - report.logError("Data size must be between 1 and 4 bytes", inst, "dataSize"); + if (inst["dataSize"] < 1 || inst["dataSize"] > 8) { + report.logError("Data size must be between 1 and 8 bytes", inst, "dataSize"); } // Validate offset against memory reserve size @@ -98,11 +98,25 @@ function getMacro(pruInstructionMacro, opCode) { const isWrite = opCode.includes("store") || opCode.includes("write"); // Generate macro DEFINITION when called with empty pruInstructionMacro + const is64 = opCode.includes("_64"); if (pruInstructionMacro === "") { if (isWrite) { - // Store macro: dataReg, baseAddress, offset, count - macroBody = opCode + "\t.macro dataReg, baseAddress, offset, count\n"; - macroBody += `\t; Memory Store (SBBO) + if (is64) { + // 64-bit store: dataRegLo, dataRegHi, baseAddress, offset, count + macroBody = opCode + "\t.macro dataRegLo, dataRegHi, baseAddress, offset, count\n"; + macroBody += `\t; Memory Store 64-bit (SBBO burst) +\t; dataRegLo - Starting register (lower 32 bits); SBBO bursts consecutive regs +\t; dataRegHi - Upper register (passed for consistency, SBBO handles automatically) +\t; baseAddress - Base address (symbol or hex address) +\t; offset - Byte offset from base address +\t; count - Number of bytes to store +\tLDI32 TEMP_REG1, baseAddress + offset +\tSBBO &dataRegLo, TEMP_REG1, 0, count +`; + } else { + // 32-bit store: dataReg, baseAddress, offset, count + macroBody = opCode + "\t.macro dataReg, baseAddress, offset, count\n"; + macroBody += `\t; Memory Store (SBBO) \t; dataReg - Source register containing data to store \t; baseAddress - Base address (symbol or hex address) \t; offset - Byte offset from base address @@ -110,10 +124,24 @@ function getMacro(pruInstructionMacro, opCode) { \tLDI32 TEMP_REG1, baseAddress + offset \tSBBO &dataReg, TEMP_REG1, 0, count `; + } } else { - // Load macro: dataReg, baseAddress, offset, count - macroBody = opCode + "\t.macro dataReg, baseAddress, offset, count\n"; - macroBody += `\t; Memory Load (LBBO) + if (is64) { + // 64-bit load: dataRegLo, dataRegHi, baseAddress, offset, count + macroBody = opCode + "\t.macro dataRegLo, dataRegHi, baseAddress, offset, count\n"; + macroBody += `\t; Memory Load 64-bit (LBBO burst) +\t; dataRegLo - Starting register (lower 32 bits); LBBO bursts into consecutive regs +\t; dataRegHi - Upper register (passed for consistency, LBBO handles automatically) +\t; baseAddress - Base address (symbol or hex address) +\t; offset - Byte offset from base address +\t; count - Number of bytes to load +\tLDI32 TEMP_REG1, baseAddress + offset +\tLBBO &dataRegLo, TEMP_REG1, 0, count +`; + } else { + // 32-bit load: dataReg, baseAddress, offset, count + macroBody = opCode + "\t.macro dataReg, baseAddress, offset, count\n"; + macroBody += `\t; Memory Load (LBBO) \t; dataReg - Destination register to store loaded data \t; baseAddress - Base address (symbol or hex address) \t; offset - Byte offset from base address @@ -121,6 +149,7 @@ function getMacro(pruInstructionMacro, opCode) { \tLDI32 TEMP_REG1, baseAddress + offset \tLBBO &dataReg, TEMP_REG1, 0, count `; + } } macroBody += " .endm"; return macroBody; @@ -436,7 +465,11 @@ exports = { default: "m_memory_load", hidden: true, getValue: (inst) => { - return (inst["operationMode"] === "write") ? "m_memory_store" : "m_memory_load"; + const is64 = inst["dataSize"] > 4; + if (inst["operationMode"] === "write") { + return is64 ? "m_memory_store_64" : "m_memory_store"; + } + return is64 ? "m_memory_load_64" : "m_memory_load"; } }, { @@ -505,7 +538,9 @@ exports = { getValue: (inst) => { // Only return size for read mode (which has output) // Write mode has no output, return 0 - return (inst["operationMode"] === "read") ? inst["dataSize"] : 0; + if (inst["operationMode"] !== "read") return 0; + // Round up to 8 for any dataSize > 4 to ensure two full registers are reserved + return inst["dataSize"] > 4 ? 8 : inst["dataSize"]; } }, { @@ -514,23 +549,25 @@ exports = { hidden: true, getValue: (inst) => { // For write mode, input1 is the data to store - // For read mode with register offset, input1 is the offset (small value) if (inst["operationMode"] === "write") { - return inst["dataSize"]; + return inst["dataSize"] > 4 ? 8 : inst["dataSize"]; } return 0; } } ], ports: (inst) => { - let ports = []; + const dataSize = inst["dataSize"] || 4; + const ports = []; if (inst["operationMode"] === "write") { // Write mode: input for data to store - ports.push({ name: "input1", displayName: "data", type: "input" }); + const inputType = dataSize > 4 ? "input64" : "input32"; + ports.push({ name: "input1", displayName: "data", type: inputType }); } else { // Read mode: output for loaded data - ports.push({ name: "output1", displayName: "data", type: "output" }); + const outputType = dataSize > 4 ? "output64" : "output32"; + ports.push({ name: "output1", displayName: "data", type: outputType }); } // Control flow ports From 04b0aeb5fc170ed8118c00bf3a9290adf5ed17fe Mon Sep 17 00:00:00 2001 From: Ayushman Date: Mon, 8 Jun 2026 16:29:42 +0530 Subject: [PATCH 3/5] am243x: add tamagawa example - add tamagawa example - update uart_crc example to use uart config and operation Signed-off-by: Ayushman --- .../am243x-lp/icss_g0_pru1_fw/example.syscfg | 130 +++++++ .../ti-pru-cgt/example.projectspec | 83 ++++ .../icss_g0_pru1_fw/ti-pru-cgt/linker.cmd | 55 +++ .../icss_g0_pru1_fw/ti-pru-cgt/makefile | 55 +++ .../ti-pru-cgt/makefile_projectspec | 16 + .../tamagawa_single_channel/firmware/main.asm | 47 +++ .../images/tamagawa_response.png | Bin 0 -> 12609 bytes examples/tamagawa_single_channel/makefile | 105 ++++++ .../r5fss0-0_freertos/example.syscfg | 295 +++++++++++++++ .../am243x-lp/r5fss0-0_freertos/main.c | 84 +++++ .../ti-arm-clang/example.projectspec | 115 ++++++ .../r5fss0-0_freertos/ti-arm-clang/makefile | 354 ++++++++++++++++++ .../ti-arm-clang/makefile_ccs_bootimage_gen | 113 ++++++ .../ti-arm-clang/makefile_projectspec | 16 + .../ti-arm-clang/syscfg_c.rov.xs | 12 + .../mcuplus/empty_example.c | 208 ++++++++++ examples/tamagawa_single_channel/readme.md | 274 ++++++++++++++ .../am243x-lp/icss_g0_pru0_fw/example.syscfg | 85 +++-- .../am243x-lp/icss_g0_pru1_fw/example.syscfg | 67 ++-- .../am261x-lp/icss_m1_pru0_fw/example.syscfg | 85 +++-- .../am261x-lp/icss_m1_pru1_fw/example.syscfg | 24 +- examples/uart_crc/firmware/main.asm | 2 +- examples/uart_crc/readme.md | 173 ++++++++- 23 files changed, 2288 insertions(+), 110 deletions(-) create mode 100644 examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg create mode 100644 examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/example.projectspec create mode 100644 examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/linker.cmd create mode 100644 examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile create mode 100644 examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile_projectspec create mode 100644 examples/tamagawa_single_channel/firmware/main.asm create mode 100644 examples/tamagawa_single_channel/images/tamagawa_response.png create mode 100644 examples/tamagawa_single_channel/makefile create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/example.syscfg create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/main.c create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_ccs_bootimage_gen create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_projectspec create mode 100644 examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/syscfg_c.rov.xs create mode 100644 examples/tamagawa_single_channel/mcuplus/empty_example.c create mode 100644 examples/tamagawa_single_channel/readme.md diff --git a/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg new file mode 100644 index 0000000..57cc415 --- /dev/null +++ b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg @@ -0,0 +1,130 @@ +/** + * These arguments were used when this file was generated. They will be automatically applied on subsequent loads + * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments. + * @cliArgs --device "AM243x_ALX_beta" --part "ALX" --package "ALX" --context "icss_g0_pru1" --product "PRU_NO_CODE_TOOL@01.00.01" + * @v2CliArgs --device "AM2434" --package "FCCSP (ALX)" --variant "AM2434-E" --context "icss_g0_pru1" --product "PRU_NO_CODE_TOOL@01.00.01" + * @versions {"tool":"1.27.0+4565"} + */ + +/** + * Import the modules used in this configuration. + */ +const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); +const load_constant_block1 = load_constant_block.addInstance(); +const load_constant_block3 = load_constant_block.addInstance(); +const conditional_block = scripting.addModule("/pru_blocks/program_control/conditional_block", {}, false); +const conditional_block1 = conditional_block.addInstance(); +const loop_block = scripting.addModule("/pru_blocks/program_control/loop_block", {}, false); +const loop_block1 = loop_block.addInstance(); +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +const uart_rx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx_op", {}, false); +const uart_rx_op1 = uart_rx_op.addInstance(); +const uart_tx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx_op", {}, false); +const uart_tx_op1 = uart_tx_op.addInstance(); +const delay_block = scripting.addModule("/pru_blocks/utils/delay_block", {}, false); +const delay_block1 = delay_block.addInstance(); +const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); +const memory_access_block1 = memory_access_block.addInstance(); +const memory_access_block2 = memory_access_block.addInstance(); +const memory_access_block3 = memory_access_block.addInstance(); +const memory_variable_block = scripting.addModule("/pru_blocks/utils/memory_variable_block", {}, false); +const memory_variable_block1 = memory_variable_block.addInstance(); + +/** + * Write custom configuration values to the imported modules. + */ +load_constant_block1.constant1 = 1; +load_constant_block1.$name = "trigger_true"; + +load_constant_block3.$name = "trigger_clear_value"; + +conditional_block1.$name = "If_Else_0"; +conditional_block1.conditionToCheck = "equalToInput2"; + +loop_block1.infiniteLoop = true; +loop_block1.$name = "repeat_always"; +loop_block1.$size = [1000,515]; + +uart_rx_op1.$name = "rx_encoder"; +uart_rx_op1.rxFrameSize = 62; + +uart_tx_op1.$name = "tx"; + +uart_rx_op1.uartConfig = uart_config1; +uart_tx_op1.uartConfig = uart_config1; +uart_config1.txClockSource = 1; +uart_config1.txBaudRate = 2.5; +uart_config1.txStartBitPolarity = 0; +uart_config1.txStopBitPolarity = 1; +uart_config1.rxChannel = 0; +uart_config1.rxClockSource = 1; +uart_config1.rxBaudRate = 2.5; +uart_config1.rxStartBitPolarity = 0; +uart_config1.$name = "config"; + +delay_block1.$name = "Delay_0"; + +memory_access_block1.$name = "trigger"; +memory_access_block1.dataSize = 1; + +memory_access_block2.operationMode = "write"; +memory_access_block2.offsetValue = 0x4; +memory_access_block2.$name = "write_response"; +memory_access_block2.dataSize = 8; + +memory_access_block3.operationMode = "write"; +memory_access_block3.$name = "clear_trigger"; +memory_access_block3.dataSize = 1; + +const memory_access_block4 = memory_access_block.addInstance(); +memory_access_block4.$name = "read_command"; +memory_access_block4.offsetValue = 0x1; +memory_access_block4.dataSize = 1; + +memory_variable_block1.$name = "Memory_Variable_0"; +memory_variable_block1.sizeInBytes = 16; +memory_variable_block1.memoryLocation = "smem"; +memory_access_block1.memVar = memory_variable_block1; +memory_access_block2.memVar = memory_variable_block1; +memory_access_block3.memVar = memory_variable_block1; +memory_access_block4.memVar = memory_variable_block1; + +/** + * References between modules. + */ +loop_block1.$groupContents = [memory_access_block1, conditional_block1, load_constant_block1, uart_config1, memory_access_block4, uart_tx_op1, uart_rx_op1, memory_access_block2, memory_access_block3, load_constant_block3, delay_block1]; + +/** + * Connections between modules. + */ +scripting.connect(load_constant_block1, "output1", conditional_block1, "input2"); +scripting.connect(memory_access_block4, "output1", uart_tx_op1, "input1"); +scripting.connect(memory_access_block4, "prev", uart_config1, "next"); +scripting.connect(memory_access_block4, "next", uart_tx_op1, "prev"); +scripting.connect(load_constant_block3, "output1", memory_access_block3, "input1"); +scripting.connect(load_constant_block3, "prev", memory_access_block2, "next"); +scripting.connect(load_constant_block3, "next", memory_access_block3, "prev"); +scripting.connect(conditional_block1, "input1", memory_access_block1, "output1"); +scripting.connect(conditional_block1, "T", uart_config1, "prev"); +scripting.connect(conditional_block1, "F", delay_block1, "prev"); +scripting.connect(uart_rx_op1, "prev", uart_tx_op1, "next"); +scripting.connect(uart_rx_op1, "next", memory_access_block2, "prev"); +scripting.connect(uart_rx_op1, "output1", memory_access_block2, "input1"); + +/** + * (x,y) coordinates for modules that are displayed in a graph. + */ +load_constant_block1.$position = [30,240]; +load_constant_block3.$position = [825,190]; +memory_access_block4.$position = [405,305]; +conditional_block1.$position = [150,180]; +loop_block1.$position = [-510,-40]; +uart_rx_op1.$position = [655,320]; +uart_tx_op1.$position = [540,310]; +uart_config1.$position = [300,280]; +delay_block1.$position = [385,185]; +memory_access_block1.$position = [35,165]; +memory_access_block2.$position = [790,355]; +memory_access_block3.$position = [905,145]; +memory_variable_block1.$position = [-805,-60]; diff --git a/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/example.projectspec b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/example.projectspec new file mode 100644 index 0000000..23ab8b9 --- /dev/null +++ b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/example.projectspec @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/linker.cmd b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/linker.cmd new file mode 100644 index 0000000..11b7846 --- /dev/null +++ b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/linker.cmd @@ -0,0 +1,55 @@ +/* + * AM243x_PRU1.cmd + * + * Example Linker command file for linking assembly programs built with the TI-PRU-CGT + * on AM243x PRU1 cores + */ + +/* Specify the System Memory Map */ +MEMORY +{ + PAGE 0: + /* 12 KB PRU Instruction RAM */ + PRU_IMEM : org = 0x00000000 len = 0x00003000 + + PAGE 1: + /* Data RAMs */ + /* 8 KB PRU Data RAM 1; use only the first 4 KB for PRU1 and reserve + * the second 4 KB for RTU1 and Tx_PRU1 */ + PRU1_DMEM_1 : org = 0x00000000 len = 0x00001000 + /* 8 KB PRU Data RAM 0; reserved completely for Slice0 cores - PRU0, + * RTU0 and Tx_PRU0; do not use for any Slice1 cores */ + PRU0_DMEM_0 : org = 0x00002000 len = 0x00001000 + /* NOTE: Custom split of the second 4 KB of ICSS Data RAMs 0 and 1 + * split equally between the corresponding RTU and Tx_PRU cores in + * each slice */ + RTU1_DMEM_1 : org = 0x00001000 len = 0x00000800 + TX_PRU1_DMEM_1 : org = 0x00001800 len = 0x00000800 + RTU0_DMEM_0 : org = 0x00003000 len = 0x00000800 + TX_PRU0_DMEM_0 : org = 0x00003800 len = 0x00000800 + + PAGE 2: + /* C28 needs to be programmed to point to SHAREDMEM, default is 0 */ + /* 64 KB PRU Shared RAM */ + PRU_SHAREDMEM : org = 0x00010000 len = 0x00010000 +} + +/* Specify the sections allocation into memory */ +SECTIONS { + .text:main > 0, PAGE 0 + .text > PRU_IMEM, PAGE 0 + .stack > PRU1_DMEM_1, PAGE 1 + .bss > PRU1_DMEM_1, PAGE 1 + .udmem > PRU1_DMEM_1, PAGE 1 + .usmem > PRU_SHAREDMEM, PAGE 2 + .initdmem > PRU1_DMEM_1, PAGE 1 + .cio > PRU1_DMEM_1, PAGE 1 + .data > PRU1_DMEM_1, PAGE 1 + .switch > PRU1_DMEM_1, PAGE 1 + .sysmem > PRU1_DMEM_1, PAGE 1 + .cinit > PRU1_DMEM_1, PAGE 1 + .rodata > PRU1_DMEM_1, PAGE 1 + .rofardata > PRU1_DMEM_1, PAGE 1 + .farbss > PRU1_DMEM_1, PAGE 1 + .fardata > PRU1_DMEM_1, PAGE 1 +} diff --git a/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile new file mode 100644 index 0000000..b006b80 --- /dev/null +++ b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile @@ -0,0 +1,55 @@ +export PRU_NO_CODE_TOOL_PATH?=$(abspath ../../../../../..) +include $(PRU_NO_CODE_TOOL_PATH)/imports.mak + +# Define build outputs +OUTPUT_NAME := tamagawa_single_channel_am243x-lp_icss_g0_pru1_fw + +# MCU+ projects: rename & move hex array output +MCU_HEX_NAME := pru1_load_bin.h +HEX_ARRAY_PREFIX := PRU1Firmware +MCU_HEX_PATH := $(PRU_NO_CODE_TOOL_PATH)/examples/tamagawa_single_channel/firmware/am243x-lp/$(MCU_HEX_NAME) + +# which directories to search for assembly & C source files? +FILES_PATH := .. ../../.. . syscfg/ + +# Linker command file +COMMAND_FILES := linker.cmd + +# Silicon version (3: PRUSS, PRU-ICSS; 4: PRU_ICSSG) +PRU_VERSION := 4 +EXPECTED_DEVICE := am243x + +# Default values are defined in pru_rules.mak: +# - include paths (INCLUDE) +# - compiler flags (CFLAGS) +# - linker flags (LFLAGS) + +# Optional: +# Pre-define here to override default value +# OPT_LEVEL, STACK_SIZE, HEAP_SIZE, or ENTRY_POINT + +# pru_rules.mak has shared settings for all PRU/RTU/TX_PRU core makefiles +include $(PRU_NO_CODE_TOOL_PATH)/pru_rules.mak + +# Optional: +# Append values to INCLUDE, CFLAGS, or LFLAGS here. +# Values can be appended like this: +# INCLUDE += --include_path= + +# Defines (pass instance specific definitions to the program) +# see --define or -D in 'PRU Optimizing C/C++ Compiler User's Guide' +# For example, to define PRU0, ICSSG1, SOC_AM243X, and set _DEBUG_=1: +# -DPRU0 -DICSSG1 --define=SOC_AM243X -D_DEBUG_=1 +DFLAGS := -DPRU0 -DPRU1 --define=SOC_AM243X + +# Libraries +# see --library in 'PRU Optimizing C/C++ Compiler User's Guide' +LIBS := + +syscfg: ../example.syscfg + @echo Generating SysConfig files ... + $(SYSCFG_NODE) $(SYSCFG_CLI_PATH)/dist/cli.js --product $(SYSCFG_PRU_NO_TOOL_PRODUCT) --device AM243x_ALX_beta --context icss_g0_pru1 --part ALX --package ALX --output syscfg/ ../example.syscfg + +syscfg-gui: + $(SYSCFG_NWJS) $(SYSCFG_PATH) --product $(SYSCFG_PRU_NO_TOOL_PRODUCT) --device AM243x_ALX_beta --context icss_g0_pru1 --part ALX --package ALX --output syscfg/ ../example.syscfg + diff --git a/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile_projectspec b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile_projectspec new file mode 100644 index 0000000..3ec57be --- /dev/null +++ b/examples/tamagawa_single_channel/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile_projectspec @@ -0,0 +1,16 @@ +export PRU_NO_CODE_TOOL_PATH?=$(abspath ../../../../../..) +include $(PRU_NO_CODE_TOOL_PATH)/imports.mak + +PROFILE?=Release + +PROJECT_NAME=tamagawa_single_channel_am243x-lp_icss_g0_pru1_fw_ti-pru-cgt + +all: + $(CCS_ECLIPSE) -noSplash -data $(PRU_NO_CODE_TOOL_PATH)/ccs_projects -application com.ti.ccstudio.apps.projectBuild -ccs.projects $(PROJECT_NAME) -ccs.configuration $(PROFILE) + +clean: + $(CCS_ECLIPSE) -noSplash -data $(PRU_NO_CODE_TOOL_PATH)/ccs_projects -application com.ti.ccstudio.apps.projectBuild -ccs.projects $(PROJECT_NAME) -ccs.configuration $(PROFILE) -ccs.clean + +export: + $(MKDIR) $(PRU_NO_CODE_TOOL_PATH)/ccs_projects + $(CCS_ECLIPSE) -noSplash -data $(PRU_NO_CODE_TOOL_PATH)/ccs_projects -application com.ti.ccstudio.apps.projectCreate -ccs.projectSpec example.projectspec -ccs.overwrite full diff --git a/examples/tamagawa_single_channel/firmware/main.asm b/examples/tamagawa_single_channel/firmware/main.asm new file mode 100644 index 0000000..a9cd705 --- /dev/null +++ b/examples/tamagawa_single_channel/firmware/main.asm @@ -0,0 +1,47 @@ +; SPDX-License-Identifier: BSD-3-Clause +; Copyright (C) 2024-2025 Texas Instruments Incorporated - http://www.ti.com/ + +;*************************************************************************************** +; File: main.asm +; +; Brief: IPC loop — waits for R5F to set trigger byte in SMEM, +; runs no-code TX/RX, stores result, clears trigger. +; +; Shared memory layout (0x30010000 = PRU1 DRAM physical base): +; offset 0x00 : uint8_t trigger (R5F writes 1; PRU clears to 0 when done) +; offset 0x01 : uint8_t reserved[3] +; offset 0x04 : uint32_t result (PRU writes RX result R0 here) +; +;*************************************************************************************** + +; CCS/makefile specific settings + .retain ; Required for building .out with assembly file + .retainrefs ; Required for building .out with assembly file + + .global main + .ref sysconfig_generated_start + .global sysconfig_generated_end + .sect ".text:main" + +IPC_BASE_ADDR .set 0x30010000 +IPC_TRIGGER_OFFSET .set 0x00 +IPC_RESULT_OFFSET .set 0x04 + +;******** +;* MAIN * +;******** + +main: + zero &r0, 120 ; Clear all registers at startup + + ; R10 = IPC base address (kept throughout, no conflict with no-code macros) + ; R11 = scratch register for IPC reads/writes + LDI32 R10, IPC_BASE_ADDR + + ; Clear trigger and result so R5F sees a clean state + LDI R11, 0 + SBBO &R11, R10, IPC_TRIGGER_OFFSET, 4 ; clears trigger + 3 reserved bytes + SBBO &R11, R10, IPC_RESULT_OFFSET, 4 ; clears result + JMP sysconfig_generated_start +sysconfig_generated_end: + halt ; should never reach here as infinite loop \ No newline at end of file diff --git a/examples/tamagawa_single_channel/images/tamagawa_response.png b/examples/tamagawa_single_channel/images/tamagawa_response.png new file mode 100644 index 0000000000000000000000000000000000000000..adbc178a49d7cfb81dc8b637cecfc92b80f68e82 GIT binary patch literal 12609 zcmeHNc~Dd57QZMhRG-FYv7jx)<+Zf6Pux)<3AjM5HHdbG7KCVNLCP*5OG1KRtF23P zoJtD{D%IdqiIxHi2?0@~BAW{Vkt7fdO9&x^kUe?dB^Z)mp*o$;w39!W%gz1n`OZ1_ zob&z8^8FU9_p~WaD2N0c3e7j?dlr|prf$Q&`?VcYPUpar~ zflr!O9o=C6`3bKdQdamrh(Bicy`6KqxM-%h3Y&L((q3N6hHrja?Zk=e#q+Nta+B%* zu|65c-xcD~IgK1r+sSRewaIzvwN59Zygh}xmr%6lT;}>mF|`@10xq@`z*Ox>7y;o9b$Y0LDw?G-8DYoapi> z$qFiJC?u#v(R4l#SAtD`r*9VWJoH<`d(fQjhY>bDXayBpE7vxc)J``J zn^5yUi%N<}eYUXfa%YaJP+AjB4E`@}2e!Vg?Z*T{@48uOPEV{o7NlkgZ?G8JOhJRU z?rsXxjh&2BZ#+{+;1>$`HQ5~G*}2^V&lYAQtSYWQ&~It4ZFdR!ybdL!2r6xm{?4+; zCEiv*14sAt_gC5bu!o9~Uyw0c7gjJCRf<-(k?4vn)s5pmS~Y)NViNynmnd|+AB9eq zUE5ZL?^W_xIcy$_D8v-x<-MJaU|f(jG3$cw;b{$I9Of%g>=QDM%}by$Pt_`&%GB~_ zsG!gXc$#l^V|Eg`Q0tCWiL3FnBQp8kK%#oUiONIu#W%`U37IY;@tU%7=HrX3+DDXB zjXSom8XuErgFwr9&f%A(%P^8S0a@E6W0FE2wYp30+b1ern!msm3Kd0F;l(J%o}`-M zPS$j4^N{*U+lrPPoEt5qMG{^ej>Y%wq0sw&>x`1!UxwSx7+9zqD!|>8g|K;tgA$*_ zmADBLD5%7;hod*LtVm-z{7ZFI$DoOJ+Ruu>6NUX*k?`WU#uxBnvcf=4s|#o6Zx(E( zF~g{<1(NU=uX=%ae>PjJ9E>oFT_B1bkzTywX)AD}G<@e_^ru!_+O9XD!-HWfgYB#x zos=`tQ0eFo*KMsl-j1Af`WU(<+BhADUdMD2^R^9K4Upy69RdI>tPKxxg zpKI=9ptdx6>6J!*1V)Wu;WUpAX4k>GtOyFc7T&p@n{Y~=^qh-EtQ2yzRQQY+ z^CcBdCToB#VW@S)p*X=+rc(ezc`>(%;MBCh*z#zqR#6a6e#5@e8KV%f9{OPD3im7C zMiw6^KZjG^l9n-43TZWikV;qnFZv@GF>%?~`zsOJr^-_tg*ruqM+O2|5-&-HId00H zicu9Jw=K*qaBSo>4?_^Cwyi8frso{TEh`adS0r=h#JS!|`hGB@wWR}<| zagD(6Ks=eEf#C%c|Frv(=xX&a?&!;uaSuDvM5(&LX^i)Kh_myGlhtA-%Rju8mK-Pf zAZgol1a+0~6pvZ@(`V04i4~U}J9-cZA1&$W=$NLffAl$w$57&%JON6S93%Mv) zd&NgY(qT9RTd4L&!@*V$B!~kDe3jn_z~o+_pnIs@!>-xpqjyo>)cf=9ByTkYXVOwQ zc`*(tr3r$I#<;V5nSKRWnGTd5fwqP^KQSCPT?1bu^Nm0OtlcjOp^p~(ka`wmBj{fv zwbHsswB0k-ZqZ4Z$Ob|c1ksC+9EplR{|nH`4O!+jO)^M9|C)JbCr%XimP)z0SCCrU zUX)uk444V!e)HxKF;G`S{d-UoIN}uHDKk2%?2Hb4(|BYSTVZ}cpd4lkoU8GI6J}_@ zC~8j-PhlE=(?~Y~F8F&N2B*E9#HPS>0Kn4Ug|XwFbZguPO-Yp!nr%LW0u4wM*nqLK zm_pBSEz#O2`i}+I<8n3LE*7hN7-4bTkrd5hMQ~bcaWs$yTVm02P&lxJYf#7;KPiJ% z^iu{$=aTE*OgM^+(gU`^^C-;)as)bl(Rq!(0kX|{JfH$_vO?1h*M~vaUF}Y5KPZRt z4V4S{IJzmXH80ATS|hy32uYm|DA}F?JulJ3ZAO5{fF(+fGGG4%ZAtpy9Kbc*zzo2T zBV`Z8@iC#B1g`iMv>ry=HkRO&H4)&osK}xs*an8%coq~eCMSS(^A83}aWUfdbH{6J z_TqVl68;Ryjs56yMg8?_g9^?QLf7UnMZ!mn6ZCMUhKmi=h`b+?il;G7e3!fKO$IXY z8--?-pbp@3X?R#{Bj;hNhImrid5o)bgA=UNRaFbi4_Cl~IayR> zQPJ=~=mb?ZOF3!6E}cb178O08e}tp#A4*4r+zy7O{VqR)wgS|?@!5zf^s8*I04J3S@R(XqA3XaZa^0;pd+zeWhv&)I3eva_bk=;TPV(U&v(i3Fp0 zn~~Ym`<2^#QXeJi3v`CDn=Gf{lLL?%z8WS{;JZ;t&5C;9nGw?Xd%vj0R4>j{;Ip^v QgXP$`0lU6n?T+;S1N6mdZ2$lO literal 0 HcmV?d00001 diff --git a/examples/tamagawa_single_channel/makefile b/examples/tamagawa_single_channel/makefile new file mode 100644 index 0000000..720eec3 --- /dev/null +++ b/examples/tamagawa_single_channel/makefile @@ -0,0 +1,105 @@ +include ../../imports.mak + +####################### +# project information # +####################### + +PROJECT_NAME := tamagawa_single_channel +SUPPORTED_PROCESSORS := am243x am261x +# Does the PRU code have dependencies outside of the pru-no-code-tool repo? +PRU_DEPENDENCIES := +# Use NON_PRU_DEPENDENCIES to select which makefiles to call for non-PRU cores +NON_PRU_DEPENDENCIES := mcuplus + +################### +# Prebuild checks # +################### +BUILD_PROJECT := y +DEVICE_NON_PRU := + +# Only build project if $(DEVICE) is in $(SUPPORTED_PROCESSORS) +ifeq (,$(findstring $(DEVICE),$(SUPPORTED_PROCESSORS))) +BUILD_PROJECT := n +MESSAGE := Project $(PROJECT_NAME) does not have a build option for $(DEVICE) +endif + +# Only build project if PRU dependencies exist +ifneq (,$(PRU_DEPENDENCIES)) +# MCU+ SDK dependency? +ifeq (mcuplus,$(findstring mcuplus,$(PRU_DEPENDENCIES))) +ifneq ($(BUILD_MCUPLUS),y) +BUILD_PROJECT := n +MESSAGE ?= Project $(PROJECT_NAME) depends on MCU+ SDK, but BUILD_MCUPLUS != y. +endif +endif +# Additional PRU dependency checks go here. For example: +#ifeq (dependency,$(findstring dependency,$(PRU_DEPENDENCIES))) +#if (dependency check fails) +#BUILD_PROJECT := n +#MESSAGE ?= Project $(PROJECT_NAME) depends on dependency, but dependency does not exist. +#endif +#endif +endif + +# Only build non-PRU code if the non-PRU code is enabled in imports.mak +ifeq ($(BUILD_MCUPLUS),y) +ifeq (mcuplus,$(findstring mcuplus,$(NON_PRU_DEPENDENCIES))) +DEVICE_NON_PRU += $(DEVICE)_mcuplus +endif +endif +ifeq ($(BUILD_LINUX),y) +ifeq (linux,$(findstring linux,$(NON_PRU_DEPENDENCIES))) +DEVICE_NON_PRU += $(DEVICE)_linux +endif +endif + +########################### +# Make and clean commands # +########################### + +ifeq ($(BUILD_PROJECT),y) +# "make" or "make all" builds projects that match $(DEVICE) set in imports.mak +# "make" builds PRU firmware first, then host (non-PRU) code +all: MESSAGE = "Building $(PROJECT_NAME) for $(DEVICE)" +all: pre_build_message + $(MAKE) pru + $(MAKE) host + +# "make pru" builds only PRU firmware for $(DEVICE) +pru: ARGUMENTS_PRU = all +pru: $(DEVICE) + +# "make host" builds only host (non-PRU) code for $(DEVICE) +host: ARGUMENTS_MCUPLUS = all +host: $(DEVICE_NON_PRU) + +# "make clean" cleans projects that match $(DEVICE) set in imports.mak +clean: ARGUMENTS_PRU = clean +clean: ARGUMENTS_MCUPLUS = scrub +clean: MESSAGE = "Cleaning $(PROJECT_NAME) for $(DEVICE)" +clean: pre_build_message $(DEVICE) $(DEVICE_NON_PRU) + +else +# if a prebuild check failed, print message and exit +all clean pru host: pre_build_message +endif + +pre_build_message: + @echo $(MESSAGE) + +###################### +# Target definitions # +###################### + +# provide target definitions for each supported processor +# PRU firmware should be built before any RTOS code that includes it +am243x: +# am243x-lp + $(MAKE) -C firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt $(ARGUMENTS_PRU) + +am243x_mcuplus: +# am243x-lp + $(MAKE) -C mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang $(ARGUMENTS_MCUPLUS) + +.PHONY: all clean pru host pre_build_message $(DEVICE) $(DEVICE_NON_PRU) $(SUPPORTED_PROCESSORS) + diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/example.syscfg b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/example.syscfg new file mode 100644 index 0000000..adfbaaa --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/example.syscfg @@ -0,0 +1,295 @@ +/** + * These arguments were used when this file was generated. They will be automatically applied on subsequent loads + * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments. + * @cliArgs --device "AM243x_ALX_beta" --part "ALX" --package "ALX" --context "r5fss0-0" --product "PRU_NO_CODE_TOOL@01.00.01" --product "MCU_PLUS_SDK_AM243x@11.01.00" + * @v2CliArgs --device "AM2434" --package "FCCSP (ALX)" --variant "AM2434-E" --context "r5fss0-0" --product "PRU_NO_CODE_TOOL@01.00.01" --product "MCU_PLUS_SDK_AM243x@11.01.00" + * @versions {"tool":"1.27.0+4565"} + */ + +/** + * Import the modules used in this configuration. + */ +const gpio = scripting.addModule("/drivers/gpio/gpio", {}, false); +const gpio1 = gpio.addInstance(); +const pruicss = scripting.addModule("/drivers/pruicss/pruicss", {}, false); +const pruicss1 = pruicss.addInstance(); +const debug_log = scripting.addModule("/kernel/dpl/debug_log"); +const dpl_cfg = scripting.addModule("/kernel/dpl/dpl_cfg"); +const mpu_armv7 = scripting.addModule("/kernel/dpl/mpu_armv7", {}, false); +const mpu_armv71 = mpu_armv7.addInstance(); +const mpu_armv72 = mpu_armv7.addInstance(); +const mpu_armv73 = mpu_armv7.addInstance(); +const mpu_armv74 = mpu_armv7.addInstance(); +const mpu_armv75 = mpu_armv7.addInstance(); +const default_linker = scripting.addModule("/memory_configurator/default_linker", {}, false); +const default_linker1 = default_linker.addInstance(); +const general = scripting.addModule("/memory_configurator/general", {}, false); +const general1 = general.addInstance(); +const region = scripting.addModule("/memory_configurator/region", {}, false); +const region1 = region.addInstance(); +const section = scripting.addModule("/memory_configurator/section", {}, false); +const section1 = section.addInstance(); +const section2 = section.addInstance(); +const section3 = section.addInstance(); +const section4 = section.addInstance(); +const section5 = section.addInstance(); +const section6 = section.addInstance(); +const section7 = section.addInstance(); +const section8 = section.addInstance(); +const section9 = section.addInstance(); +const section10 = section.addInstance(); +const section11 = section.addInstance(); + +/** + * Write custom configuration values to the imported modules. + */ +gpio1.$name = "TAMAGAWA0_BP_POWER_EN0"; +gpio1.pinDir = "OUTPUT"; +gpio1.defaultValue = "1"; +gpio1.pu_pd = "pu"; +gpio1.GPIO_n.$assign = "MMC1_SDWP"; + +pruicss1.$name = "CONFIG_PRU_ICSS0"; +pruicss1.AdditionalICSSSettings[0].$name = "CONFIG_PRU_ICSS_IO0"; +pruicss1.AdditionalICSSSettings[0].PruGPIO.create(1); +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].$name = "CONFIG_PRU_ICSS_GPIO1"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].pruMuxMode = "1"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.$assign = "PRU_ICSSG0_PRU1"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI0.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI0.$assign = "PRG0_PRU1_GPO0"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI1.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI1.$assign = "PRG0_PRU1_GPO1"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI10.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI12.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI13.$used = true; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI14.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI15.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI16.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI17.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI18.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI19.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI2.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI2.$assign = "PRG0_PRU1_GPO2"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI3.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI4.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI5.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI6.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI7.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI8.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI9.rx = false; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPO0.$used = true; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPO1.$used = true; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPO2.$used = true; + +debug_log.enableUartLog = true; +debug_log.uartLog.$name = "CONFIG_UART_CONSOLE"; +debug_log.uartLog.UART.$assign = "USART0"; + +const uart_v0_template = scripting.addModule("/drivers/uart/v0/uart_v0_template", {}, false); +const uart_v0_template1 = uart_v0_template.addInstance({}, false); +uart_v0_template1.$name = "drivers_uart_v0_uart_v0_template0"; +debug_log.uartLog.child = uart_v0_template1; + +mpu_armv71.$name = "CONFIG_MPU_REGION0"; +mpu_armv71.size = 31; +mpu_armv71.attributes = "Device"; +mpu_armv71.accessPermissions = "Supervisor RD+WR, User RD"; +mpu_armv71.allowExecute = false; + +mpu_armv72.$name = "CONFIG_MPU_REGION1"; +mpu_armv72.size = 15; +mpu_armv72.accessPermissions = "Supervisor RD+WR, User RD"; + +mpu_armv73.$name = "CONFIG_MPU_REGION2"; +mpu_armv73.baseAddr = 0x41010000; +mpu_armv73.size = 15; +mpu_armv73.accessPermissions = "Supervisor RD+WR, User RD"; + +mpu_armv74.$name = "CONFIG_MPU_REGION3"; +mpu_armv74.accessPermissions = "Supervisor RD+WR, User RD"; +mpu_armv74.baseAddr = 0x70000000; +mpu_armv74.size = 21; + +mpu_armv75.$name = "CONFIG_MPU_REGION4"; +mpu_armv75.baseAddr = 0x60000000; +mpu_armv75.size = 28; +mpu_armv75.accessPermissions = "Supervisor RD, User RD"; + +default_linker1.$name = "memory_configurator_default_linker0"; + +general1.$name = "CONFIG_GENERAL0"; +general1.linker.$name = "TIARMCLANG0"; + +region1.$name = "MEMORY_REGION_CONFIGURATION0"; +region1.memory_region.create(9); +region1.memory_region[0].type = "TCMA_R5F"; +region1.memory_region[0].$name = "R5F_VECS"; +region1.memory_region[0].size = 0x40; +region1.memory_region[0].auto = false; +region1.memory_region[1].type = "TCMA_R5F"; +region1.memory_region[1].$name = "R5F_TCMA"; +region1.memory_region[1].size = 0x7FC0; +region1.memory_region[2].type = "TCMB_R5F"; +region1.memory_region[2].$name = "R5F_TCMB0"; +region1.memory_region[2].size = 0x8000; +region1.memory_region[3].$name = "NON_CACHE_MEM"; +region1.memory_region[3].auto = false; +region1.memory_region[3].manualStartAddress = 0x70060000; +region1.memory_region[3].size = 0x8000; +region1.memory_region[4].$name = "MSRAM"; +region1.memory_region[4].auto = false; +region1.memory_region[4].manualStartAddress = 0x70080000; +region1.memory_region[4].size = 0x40000; +region1.memory_region[5].type = "FLASH"; +region1.memory_region[5].$name = "FLASH"; +region1.memory_region[5].auto = false; +region1.memory_region[5].manualStartAddress = 0x60100000; +region1.memory_region[5].size = 0x80000; +region1.memory_region[6].$name = "USER_SHM_MEM"; +region1.memory_region[6].auto = false; +region1.memory_region[6].manualStartAddress = 0x701D0000; +region1.memory_region[6].size = 0x180; +region1.memory_region[6].isShared = true; +region1.memory_region[6].shared_cores = ["m4fss0-0","r5fss0-1","r5fss1-0","r5fss1-1"]; +region1.memory_region[7].auto = false; +region1.memory_region[7].manualStartAddress = 0x701D0180; +region1.memory_region[7].size = 0x3E80; +region1.memory_region[7].$name = "LOG_SHM_MEM"; +region1.memory_region[7].isShared = true; +region1.memory_region[7].shared_cores = ["m4fss0-0","r5fss0-1","r5fss1-0","r5fss1-1"]; +region1.memory_region[8].auto = false; +region1.memory_region[8].manualStartAddress = 0x701D4000; +region1.memory_region[8].size = 0xC000; +region1.memory_region[8].$name = "RTOS_NORTOS_IPC_SHM_MEM"; +region1.memory_region[8].isShared = true; +region1.memory_region[8].shared_cores = ["m4fss0-0","r5fss0-1","r5fss1-0","r5fss1-1"]; + +section1.$name = "Vector Table"; +section1.load_memory = "R5F_VECS"; +section1.group = false; +section1.output_section.create(1); +section1.output_section[0].$name = ".vectors"; +section1.output_section[0].palignment = true; + +section2.$name = "Text Segments"; +section2.load_memory = "MSRAM"; +section2.output_section.create(5); +section2.output_section[0].$name = ".text.hwi"; +section2.output_section[0].palignment = true; +section2.output_section[1].$name = ".text.cache"; +section2.output_section[1].palignment = true; +section2.output_section[2].$name = ".text.mpu"; +section2.output_section[2].palignment = true; +section2.output_section[3].$name = ".text.boot"; +section2.output_section[3].palignment = true; +section2.output_section[4].$name = ".text:abort"; +section2.output_section[4].palignment = true; + +section3.$name = "Code and Read-Only Data"; +section3.load_memory = "MSRAM"; +section3.output_section.create(2); +section3.output_section[0].$name = ".text"; +section3.output_section[0].palignment = true; +section3.output_section[1].$name = ".rodata"; +section3.output_section[1].palignment = true; + +section4.$name = "Data Segment"; +section4.load_memory = "MSRAM"; +section4.output_section.create(1); +section4.output_section[0].$name = ".data"; +section4.output_section[0].palignment = true; + +section5.$name = "Memory Segments"; +section5.load_memory = "MSRAM"; +section5.output_section.create(3); +section5.output_section[0].$name = ".bss"; +section5.output_section[0].palignment = true; +section5.output_section[0].output_sections_start = "__BSS_START"; +section5.output_section[0].output_sections_end = "__BSS_END"; +section5.output_section[1].$name = ".sysmem"; +section5.output_section[1].palignment = true; +section5.output_section[2].$name = ".stack"; +section5.output_section[2].palignment = true; + +section6.$name = "Stack Segments"; +section6.load_memory = "MSRAM"; +section6.output_section.create(5); +section6.output_section[0].$name = ".irqstack"; +section6.output_section[0].output_sections_start = "__IRQ_STACK_START"; +section6.output_section[0].output_sections_end = "__IRQ_STACK_END"; +section6.output_section[0].input_section.create(1); +section6.output_section[0].input_section[0].$name = ". = . + __IRQ_STACK_SIZE;"; +section6.output_section[1].$name = ".fiqstack"; +section6.output_section[1].output_sections_start = "__FIQ_STACK_START"; +section6.output_section[1].output_sections_end = "__FIQ_STACK_END"; +section6.output_section[1].input_section.create(1); +section6.output_section[1].input_section[0].$name = ". = . + __FIQ_STACK_SIZE;"; +section6.output_section[2].$name = ".svcstack"; +section6.output_section[2].output_sections_start = "__SVC_STACK_START"; +section6.output_section[2].output_sections_end = "__SVC_STACK_END"; +section6.output_section[2].input_section.create(1); +section6.output_section[2].input_section[0].$name = ". = . + __SVC_STACK_SIZE;"; +section6.output_section[3].$name = ".abortstack"; +section6.output_section[3].output_sections_start = "__ABORT_STACK_START"; +section6.output_section[3].output_sections_end = "__ABORT_STACK_END"; +section6.output_section[3].input_section.create(1); +section6.output_section[3].input_section[0].$name = ". = . + __ABORT_STACK_SIZE;"; +section6.output_section[4].$name = ".undefinedstack"; +section6.output_section[4].output_sections_start = "__UNDEFINED_STACK_START"; +section6.output_section[4].output_sections_end = "__UNDEFINED_STACK_END"; +section6.output_section[4].input_section.create(1); +section6.output_section[4].input_section[0].$name = ". = . + __UNDEFINED_STACK_SIZE;"; + +section7.$name = "Initialization and Exception Handling"; +section7.load_memory = "MSRAM"; +section7.output_section.create(3); +section7.output_section[0].$name = ".ARM.exidx"; +section7.output_section[0].palignment = true; +section7.output_section[1].$name = ".init_array"; +section7.output_section[1].palignment = true; +section7.output_section[2].$name = ".fini_array"; +section7.output_section[2].palignment = true; + +section8.$name = "User Shared Memory"; +section8.type = "NOLOAD"; +section8.load_memory = "USER_SHM_MEM"; +section8.group = false; +section8.output_section.create(1); +section8.output_section[0].$name = ".bss.user_shared_mem"; +section8.output_section[0].alignment = 0; + +section9.$name = "Log Shared Memory"; +section9.load_memory = "LOG_SHM_MEM"; +section9.type = "NOLOAD"; +section9.group = false; +section9.output_section.create(1); +section9.output_section[0].$name = ".bss.log_shared_mem"; +section9.output_section[0].alignment = 0; + +section10.$name = "IPC Shared Memory"; +section10.type = "NOLOAD"; +section10.load_memory = "RTOS_NORTOS_IPC_SHM_MEM"; +section10.group = false; +section10.output_section.create(1); +section10.output_section[0].$name = ".bss.ipc_vring_mem"; +section10.output_section[0].alignment = 0; + +section11.$name = "Non Cacheable Memory"; +section11.load_memory = "NON_CACHE_MEM"; +section11.group = false; +section11.type = "NOLOAD"; +section11.output_section.create(1); +section11.output_section[0].$name = ".bss.nocache"; +section11.output_section[0].alignment = 0; + +/** + * Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future + * version of the tool will not impact the pinmux you originally saw. These lines can be completely deleted in order to + * re-solve from scratch. + */ +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPI13.$suggestSolution = "PRG0_PRU1_GPO13"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPO0.$suggestSolution = "PRG0_PRU1_GPO0"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPO1.$suggestSolution = "PRG0_PRU1_GPO1"; +pruicss1.AdditionalICSSSettings[0].PruGPIO[0].PRU_ICSSG0_PRU.GPO2.$suggestSolution = "PRG0_PRU1_GPO2"; +debug_log.uartLog.UART.RXD.$suggestSolution = "UART0_RXD"; +debug_log.uartLog.UART.TXD.$suggestSolution = "UART0_TXD"; diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/main.c b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/main.c new file mode 100644 index 0000000..b8dd06a --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/main.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024-2025 Texas Instruments Incorporated + * + * 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 Texas Instruments Incorporated 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 + * 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. + */ + +#include +#include +#include "ti_drivers_config.h" +#include "ti_board_config.h" +#include "FreeRTOS.h" +#include "task.h" + +#define MAIN_TASK_PRI (configMAX_PRIORITIES-1) + +#define MAIN_TASK_SIZE (16384U/sizeof(configSTACK_DEPTH_TYPE)) +StackType_t gMainTaskStack[MAIN_TASK_SIZE] __attribute__((aligned(32))); + +StaticTask_t gMainTaskObj; +TaskHandle_t gMainTask; + +void empty_example_main(void *args); + +void freertos_main(void *args) +{ + empty_example_main(NULL); + + vTaskDelete(NULL); +} + + +int main(void) +{ + /* init SOC specific modules */ + System_init(); + Board_init(); + + /* This task is created at highest priority, it should create more tasks and then delete itself */ + gMainTask = xTaskCreateStatic( freertos_main, /* Pointer to the function that implements the task. */ + "freertos_main", /* Text name for the task. This is to facilitate debugging only. */ + MAIN_TASK_SIZE, /* Stack depth in units of StackType_t typically uint32_t on 32b CPUs */ + NULL, /* We are not using the task parameter. */ + MAIN_TASK_PRI, /* task priority, 0 is lowest priority, configMAX_PRIORITIES-1 is highest */ + gMainTaskStack, /* pointer to stack base */ + &gMainTaskObj ); /* pointer to statically allocated task object memory */ + configASSERT(gMainTask != NULL); + + /* Start the scheduler to start the tasks executing. */ + vTaskStartScheduler(); + + /* The following line should never be reached because vTaskStartScheduler() + will only return if there was not enough FreeRTOS heap memory available to + create the Idle and (if configured) Timer tasks. Heap management, and + techniques for trapping heap exhaustion, are described in the book text. */ + DebugP_assertNoLog(0); + + return 0; +} diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec new file mode 100644 index 0000000..67033a9 --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile new file mode 100644 index 0000000..2fb57f8 --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile @@ -0,0 +1,354 @@ +# +# Auto generated makefile +# + +export PRU_NO_CODE_TOOL_PATH?=$(abspath ../../../../../..) +include $(PRU_NO_CODE_TOOL_PATH)/imports.mak +include $(MCU_PLUS_SDK_PATH)/devconfig/devconfig.mak + + +CG_TOOL_ROOT=$(CGT_TI_ARM_CLANG_PATH) + +CC=$(CG_TOOL_ROOT)/bin/tiarmclang +LNK=$(CG_TOOL_ROOT)/bin/tiarmclang +STRIP=$(CG_TOOL_ROOT)/bin/tiarmstrip +OBJCOPY=$(CG_TOOL_ROOT)/bin/tiarmobjcopy +COV=$(CG_TOOL_ROOT)/bin/tiarmcov +PROFDATA=$(CG_TOOL_ROOT)/bin/tiarmprofdata +COVERAGE_PATH=$(abspath .) +ifeq ($(OS), Windows_NT) + PYTHON=python +else + PYTHON=python3 +endif + +PROFILE?=release +ConfigName:=$(PROFILE) + +OUTNAME:=tamagawa_single_channel.$(PROFILE).out +COREOUTNAME:=tamagawa_single_channel.$(PROFILE).optishare.out + +BOOTIMAGE_PATH=$(abspath .) +BOOTIMAGE_NAME:=tamagawa_single_channel.$(PROFILE).appimage +BOOTIMAGE_NAME_XIP:=tamagawa_single_channel.$(PROFILE).appimage_xip +BOOTIMAGE_NAME_SIGNED:=tamagawa_single_channel.$(PROFILE).appimage.signed +BOOTIMAGE_RPRC_NAME:=tamagawa_single_channel.$(PROFILE).rprc +BOOTIMAGE_RPRC_NAME_XIP:=tamagawa_single_channel.$(PROFILE).rprc_xip +BOOTIMAGE_RPRC_NAME_TMP:=tamagawa_single_channel.$(PROFILE).rprc_tmp +BOOTIMAGE_NAME_HS:=tamagawa_single_channel.$(PROFILE).appimage.hs +BOOTIMAGE_NAME_HS_FS:=tamagawa_single_channel.$(PROFILE).appimage.hs_fs +TARGETS := $(BOOTIMAGE_NAME) + + +FILES_common := \ + main.c \ + empty_example.c \ + ti_drivers_config.c \ + ti_drivers_open_close.c \ + ti_board_config.c \ + ti_board_open_close.c \ + ti_dpl_config.c \ + ti_pinmux_config.c \ + ti_power_clock_config.c \ + +FILES_PATH_common = \ + .. \ + ../../.. \ + generated \ + +INCLUDES_common := \ + -I${CG_TOOL_ROOT}/include/c \ + -I${MCU_PLUS_SDK_PATH}/source \ + -I${MCU_PLUS_SDK_PATH}/source/kernel/freertos/FreeRTOS-Kernel/include \ + -I${MCU_PLUS_SDK_PATH}/source/kernel/freertos/portable/TI_ARM_CLANG/ARM_CR5F \ + -I${MCU_PLUS_SDK_PATH}/source/kernel/freertos/config/am243x/r5f \ + -I${MCU_PLUS_SDK_PATH}/source/pru_io/driver \ + -I${PRU_NO_CODE_TOOL_PATH}/examples/tamagawa_single_channel/firmware/am243x-lp \ + -Igenerated \ + +DEFINES_common := \ + -DSOC_AM243X \ + -DOS_FREERTOS \ + +CFLAGS_common := \ + -mcpu=cortex-r5 \ + -mfloat-abi=hard \ + -mfpu=vfpv3-d16 \ + -mthumb \ + -Wall \ + -Werror \ + -g \ + -Wno-gnu-variable-sized-type-not-at-end \ + -Wno-unused-function \ + +CFLAGS_cpp_common := \ + -Wno-c99-designator \ + -Wno-extern-c-compat \ + -Wno-c++11-narrowing \ + -Wno-reorder-init-list \ + -Wno-register \ + -Wno-writable-strings \ + -Wno-enum-compare \ + -Wno-reserved-user-defined-literal \ + -Wno-unused-const-variable \ + -Wno-vla-cxx-extension \ + -x c++ \ + +CFLAGS_debug := \ + -D_DEBUG_=1 \ + +CFLAGS_release := \ + -Os \ + +LNK_FILES_common = \ + generated/linker.cmd \ + +LIBS_PATH_common = \ + -Wl,-i${MCU_PLUS_SDK_PATH}/source/kernel/freertos/lib \ + -Wl,-i${MCU_PLUS_SDK_PATH}/source/drivers/lib \ + -Wl,-i${MCU_PLUS_SDK_PATH}/source/board/lib \ + -Wl,-i${MCU_PLUS_SDK_PATH}/source/pru_io/lib \ + -Wl,-i${CG_TOOL_ROOT}/lib \ + +LIBS_common = \ + -lfreertos.am243x.r5f.ti-arm-clang.${ConfigName}.lib \ + -ldrivers.am243x.r5f.ti-arm-clang.freertos.${ConfigName}.lib \ + -lboard.am243x.r5f.ti-arm-clang.freertos.${ConfigName}.lib \ + -llibc.a \ + -llibsysbm.a \ + +LFLAGS_common = \ + -Wl,--diag_suppress=10063 \ + -Wl,--ram_model \ + -Wl,--reread_libs \ + -Wl,--gen_xml_func_hash \ + + +LIBS_NAME = \ + freertos.am243x.r5f.ti-arm-clang.${ConfigName}.lib \ + drivers.am243x.r5f.ti-arm-clang.freertos.${ConfigName}.lib \ + board.am243x.r5f.ti-arm-clang.freertos.${ConfigName}.lib \ + libc.a \ + libsysbm.a \ + +LIBS_PATH_NAME = \ + ${MCU_PLUS_SDK_PATH}/source/kernel/freertos/lib \ + ${MCU_PLUS_SDK_PATH}/source/drivers/lib \ + ${MCU_PLUS_SDK_PATH}/source/board/lib \ + ${MCU_PLUS_SDK_PATH}/source/pru_io/lib \ + ${CG_TOOL_ROOT}/lib \ + +FILES := $(FILES_common) $(FILES_$(PROFILE)) +ASMFILES := $(ASMFILES_common) $(ASMFILES_$(PROFILE)) +FILES_PATH := $(FILES_PATH_common) $(FILES_PATH_$(PROFILE)) +CFLAGS := $(CFLAGS_common) $(CFLAGS_$(PROFILE)) +ifeq ($(INSTRUMENTATION_MODE), yes) +CFLAGS += -fprofile-instr-generate -fcoverage-mapping +endif +DEFINES := $(DEFINES_common) $(DEFINES_$(PROFILE)) +INCLUDES := $(INCLUDES_common) $(INCLUDE_$(PROFILE)) +LIBS := $(LIBS_common) $(LIBS_$(PROFILE)) +LIBS_PATH := $(LIBS_PATH_common) $(LIBS_PATH_$(PROFILE)) +LFLAGS := $(LFLAGS_common) $(LFLAGS_$(PROFILE)) +LNKOPTFLAGS := $(LNKOPTFLAGS_common) $(LNKOPTFLAGS_$(PROFILE)) +LNK_FILES := $(LNK_FILES_common) $(LNK_FILES_$(PROFILE)) + +OBJDIR := obj/$(PROFILE)/ +OBJS := $(FILES:%.c=%.obj) +OBJS += $(ASMFILES:%.S=%.obj) +DEPS := $(FILES:%.c=%.d) + +vpath %.obj $(OBJDIR) +vpath %.c $(FILES_PATH) +vpath %.S $(FILES_PATH) +vpath %.lib $(LIBS_PATH_NAME) +vpath %.a $(LIBS_PATH_NAME) + +$(OBJDIR)/%.obj %.obj: %.c + @echo Compiling: am243x:r5fss0-0:freertos:ti-arm-clang $(OUTNAME): $< + $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) -MMD -o $(OBJDIR)/$@ $< + +$(OBJDIR)/%.obj %.obj: %.S + @echo Compiling: am243x:r5fss0-0:freertos:ti-arm-clang $(LIBNAME): $< + $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) -o $(OBJDIR)/$@ $< + +all: $(TARGETS) + +SYSCFG_GEN_FILES=generated/ti_drivers_config.c generated/ti_drivers_config.h +SYSCFG_GEN_FILES+=generated/ti_drivers_open_close.c generated/ti_drivers_open_close.h +SYSCFG_GEN_FILES+=generated/ti_dpl_config.c generated/ti_dpl_config.h +SYSCFG_GEN_FILES+=generated/ti_pinmux_config.c generated/ti_power_clock_config.c +SYSCFG_GEN_FILES+=generated/ti_board_config.c generated/ti_board_config.h +SYSCFG_GEN_FILES+=generated/ti_board_open_close.c generated/ti_board_open_close.h + +SYSTEM_FLAG ?= false + +ifeq ($(SYSTEM_FLAG), false) + SYSTEM_COMMAND := syscfg $(SYSCFG_GEN_FILES) $(OBJS) $(LNK_FILES) $(LIBS_NAME) +else + SYSTEM_COMMAND := $(OBJS) $(LNK_FILES) $(LIBS_NAME) +endif + +$(OUTNAME): $(SYSTEM_COMMAND) + @echo . + @echo Linking: am243x:r5fss0-0:freertos:ti-arm-clang $@ ... + $(LNK) $(LNKOPTFLAGS) $(LFLAGS) $(LIBS_PATH) -Wl,-m=$(basename $@).map -o $@ $(addprefix $(OBJDIR), $(OBJS)) $(LIBS) $(LNK_FILES) -Wl,--xml_link_info=$(basename $@).lnkxml + @echo Linking: am243x:r5fss0-0:freertos:ti-arm-clang $@ Done !!! + @echo . + +coreout: $(COREOUTNAME) +$(COREOUTNAME): $(SYSTEM_COMMAND) + @echo . + @echo Relinking Linking: am243x:r5fss0-0:freertos:ti-arm-clang $@ with OptiShare SSO.. + $(LNK) $(LNKOPTFLAGS) $(LFLAGS) $(LIBS_PATH) -Wl,-m=$(basename $@).map -o $@ $(addprefix $(OBJDIR), $(OBJS)) $(LIBS) $(LNK_FILES) -Wl,--xml_link_info=$(basename $@).lnkxml -Wl,--import_sso=$(SSO_PATH) + @echo Relinking with optishare: am243x:r5fss0-0:freertos:ti-arm-clang $@ Done !!! + @echo . + +clean: + @echo Cleaning: am243x:r5fss0-0:freertos:ti-arm-clang $(OUTNAME) ... + $(RMDIR) $(OBJDIR) + $(RM) $(OUTNAME) + $(RM) $(BOOTIMAGE_NAME) + $(RM) $(BOOTIMAGE_NAME_XIP) + $(RM) $(BOOTIMAGE_NAME_SIGNED) + $(RM) $(BOOTIMAGE_NAME_HS) + $(RM) $(BOOTIMAGE_NAME_HS_FS) + $(RM) $(BOOTIMAGE_RPRC_NAME) + $(RM) $(BOOTIMAGE_RPRC_NAME_XIP) + $(RMDIR) generated/ + +scrub: + @echo Scrubing: am243x:r5fss0-0:freertos:ti-arm-clang tamagawa_single_channel ... + $(RMDIR) obj +ifeq ($(OS),Windows_NT) + $(RM) \*.out + $(RM) \*.map + $(RM) \*.appimage* + $(RM) \*.rprc* + $(RM) \*.tiimage* + $(RM) \*.bin + $(RM) \*.lnkxml + $(RM) \*.ossr +else + $(RM) *.out + $(RM) *.map + $(RM) *.appimage* + $(RM) *.rprc* + $(RM) *.tiimage* + $(RM) *.bin + $(RM) *.lnkxml + $(RM) *.ossr +endif + $(RMDIR) generated + +$(OBJS): | $(OBJDIR) + +$(OBJDIR): + $(MKDIR) $@ + + +.NOTPARALLEL: + +.INTERMEDIATE: syscfg +$(SYSCFG_GEN_FILES): syscfg + +ifeq ($(SYSTEM_FLAG), false) +syscfg: ../example.syscfg + @echo Generating SysConfig files ... + $(SYSCFG_NODE) $(SYSCFG_CLI_PATH)/dist/cli.js --product $(SYSCFG_PRU_NO_TOOL_PRODUCT) --product $(SYSCFG_MCU_PLUS_SDK_PRODUCT) --context r5fss0-0 --part ALX --package ALX --output generated/ ../example.syscfg +endif + +syscfg-gui: + $(SYSCFG_NWJS) $(SYSCFG_PATH) --product $(SYSCFG_PRU_NO_TOOL_PRODUCT) --product $(SYSCFG_MCU_PLUS_SDK_PRODUCT) --device AM243x_ALX_beta --context r5fss0-0 --part ALX --package ALX --output generated/ ../example.syscfg + +# +# Generation of boot image which can be loaded by Secondary Boot Loader (SBL) +# +ifeq ($(OS),Windows_NT) +EXE_EXT=.exe +endif +ifeq ($(OS),Windows_NT) + BOOTIMAGE_CERT_GEN_CMD=powershell -executionpolicy unrestricted -command $(MCU_PLUS_SDK_PATH)/source/security/security_common/tools/boot/signing/x509CertificateGen.ps1 +else + BOOTIMAGE_CERT_GEN_CMD=$(MCU_PLUS_SDK_PATH)/source/security/security_common/tools/boot/signing/x509CertificateGen.sh +endif +BOOTIMAGE_TEMP_OUT_FILE=temp_stdout_$(PROFILE).txt + +BOOTIMAGE_CERT_KEY=$(APP_SIGNING_KEY) + +BOOTIMAGE_CORE_ID_r5fss0-0 = 4 +BOOTIMAGE_CORE_ID_r5fss0-1 = 5 +BOOTIMAGE_CORE_ID_r5fss1-0 = 6 +BOOTIMAGE_CORE_ID_r5fss1-1 = 7 +BOOTIMAGE_CORE_ID_m4fss0-0 = 14 +SBL_RUN_ADDRESS=0x70000000 +SBL_DEV_ID=55 + +MULTI_CORE_IMAGE_GEN = $(SYSCFG_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/multicoreImageGen/multicoreImageGen.js +OUTRPRC_CMD = $(SYSCFG_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/out2rprc/elf2rprc.js +APP_IMAGE_SIGN_CMD = $(MCU_PLUS_SDK_PATH)/source/security/security_common/tools/boot/signing/appimage_x509_cert_gen.py + +ifeq ($(OS),Windows_NT) + XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.exe +else + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S), Darwin) + XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.out.mac + else + XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.out +endif +endif + +MULTI_CORE_IMAGE_PARAMS = \ + $(BOOTIMAGE_RPRC_NAME)@$(BOOTIMAGE_CORE_ID_r5fss0-0) \ + +MULTI_CORE_IMAGE_PARAMS_XIP = \ + $(BOOTIMAGE_RPRC_NAME_XIP)@$(BOOTIMAGE_CORE_ID_r5fss0-0) \ + +$(BOOTIMAGE_NAME): $(OUTNAME) + @echo Boot image: am243x:r5fss0-0:freertos:ti-arm-clang $(BOOTIMAGE_PATH)/$@ ... +ifneq ($(OS),Windows_NT) + $(CHMOD) a+x $(XIPGEN_CMD) +endif + $(OUTRPRC_CMD) $(OUTNAME) >> $(BOOTIMAGE_TEMP_OUT_FILE) + $(COPY) $(BOOTIMAGE_RPRC_NAME) $(BOOTIMAGE_RPRC_NAME_TMP) + $(RM) $(BOOTIMAGE_RPRC_NAME) + $(XIPGEN_CMD) -i $(BOOTIMAGE_RPRC_NAME_TMP) -o $(BOOTIMAGE_RPRC_NAME) -x $(BOOTIMAGE_RPRC_NAME_XIP) --flash-start-addr 0x60000000 -v > $(BOOTIMAGE_TEMP_OUT_FILE) + $(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME) $(MULTI_CORE_IMAGE_PARAMS) >> $(BOOTIMAGE_TEMP_OUT_FILE) + $(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME_XIP) $(MULTI_CORE_IMAGE_PARAMS_XIP) >> $(BOOTIMAGE_TEMP_OUT_FILE) + $(RM) $(BOOTIMAGE_RPRC_NAME_TMP) + $(RM) $(BOOTIMAGE_TEMP_OUT_FILE) +ifeq ($(DEVICE_TYPE), HS) +# Sign the appimage using appimage signing script +ifeq ($(ENC_ENABLED),no) + @echo Boot image signing: Encryption is disabled. + $(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 1 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME_HS) +else + @echo Boot image signing: Encryption is enabled. + $(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 1 --key $(APP_SIGNING_KEY) --enc y --enckey $(APP_ENCRYPTION_KEY) --output $(BOOTIMAGE_NAME_HS) + $(RM) $(BOOTIMAGE_NAME)-enc +endif + $(RM) $(BOOTIMAGE_NAME) + @echo Boot image: am243x:r5fss0-0:freertos:ti-arm-clang $(BOOTIMAGE_PATH)/$(BOOTIMAGE_NAME_HS) Done !!! + @echo . +else +# Sign the appimage for HS-FS using appimage signing script + $(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 1 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME_HS_FS) + $(RM) $(BOOTIMAGE_NAME) + @echo Boot image: am243x:r5fss0-0:freertos:ti-arm-clang $(BOOTIMAGE_PATH)/$(BOOTIMAGE_NAME_HS_FS) Done !!! + @echo . +endif + + + +.PHONY: coverage +coverage: + @echo Creating Coverage Report for tamagawa_single_channel.$(PROFILE) ... + $(MKDIR) coverage + $(PROFDATA) merge -sparse -obj-file=$(OUTNAME) $(OUTNAME).cnt -o tamagawa_single_channel.$(PROFILE).profdata + $(COV) show --format=html --show-expansions --show-instantiations --show-branches=count --object=$(OUTNAME) -instr-profile=tamagawa_single_channel.$(PROFILE).profdata --output-dir=$(COVERAGE_PATH)/coverage --ignore-filename-regex=build_jenkins + $(COV) export --format=text --object=$(OUTNAME) --instr-profile=tamagawa_single_channel.$(PROFILE).profdata > coverage/tamagawa_single_channel.$(PROFILE).profdata.json + node $(MCU_PLUS_SDK_PATH)/tools/smart_placement/clang_coverage_analyse.js --input=coverage/tamagawa_single_channel.$(PROFILE).profdata.json --output-json=coverage/tamagawa_single_channel.$(PROFILE).analysis.json --output=../tamagawa_single_channel.annotations.$(PROFILE).S --top-function-count=500 + @echo Coverage Report Generated at $(COVERAGE_PATH)/coverage folder !!! + +-include $(addprefix $(OBJDIR)/, $(DEPS)) diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_ccs_bootimage_gen b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_ccs_bootimage_gen new file mode 100644 index 0000000..e6de128 --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_ccs_bootimage_gen @@ -0,0 +1,113 @@ +# +# Auto generated makefile +# + +# Below variables need to be defined outside this file or via command line +# - PRU_NO_CODE_TOOL_PATH +# - MCU_PLUS_SDK_PATH +# - PROFILE +# - CG_TOOL_ROOT +# - OUTNAME +# - CCS_INSTALL_DIR +# - CCS_IDE_MODE + +CCS_PATH=$(CCS_INSTALL_DIR) +include $(PRU_NO_CODE_TOOL_PATH)/imports.mak +include $(MCU_PLUS_SDK_PATH)/devconfig/devconfig.mak + +STRIP=$(CG_TOOL_ROOT)/bin/tiarmstrip +OBJCOPY=$(CG_TOOL_ROOT)/bin/tiarmobjcopy +ifeq ($(OS), Windows_NT) + PYTHON=python +else + PYTHON=python3 +endif + +OUTFILE=$(PROFILE)/$(OUTNAME).out +BOOTIMAGE_PATH=$(abspath ${PROFILE}) +BOOTIMAGE_NAME:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage +BOOTIMAGE_NAME_XIP:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage_xip +BOOTIMAGE_NAME_SIGNED:=$(BOOTIMAGE_PATH)/$(OUTNAME).appimage.signed +BOOTIMAGE_RPRC_NAME:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc +BOOTIMAGE_RPRC_NAME_XIP:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc_xip +BOOTIMAGE_RPRC_NAME_TMP:=$(BOOTIMAGE_PATH)/$(OUTNAME).rprc_tmp + +# +# Generation of boot image which can be loaded by Secondary Boot Loader (SBL) +# +ifeq ($(OS),Windows_NT) +EXE_EXT=.exe +endif +ifeq ($(OS),Windows_NT) + BOOTIMAGE_CERT_GEN_CMD=powershell -executionpolicy unrestricted -command $(MCU_PLUS_SDK_PATH)/source/security/security_common/tools/boot/signing/x509CertificateGen.ps1 +else + BOOTIMAGE_CERT_GEN_CMD=$(MCU_PLUS_SDK_PATH)/source/security/security_common/tools/boot/signing/x509CertificateGen.sh +endif +BOOTIMAGE_TEMP_OUT_FILE=$(PROFILE)/temp_stdout_$(PROFILE).txt + +BOOTIMAGE_CORE_ID_r5fss0-0 = 4 +BOOTIMAGE_CORE_ID_r5fss0-1 = 5 +BOOTIMAGE_CORE_ID_r5fss1-0 = 6 +BOOTIMAGE_CORE_ID_r5fss1-1 = 7 +BOOTIMAGE_CORE_ID_m4fss0-0 = 14 +SBL_RUN_ADDRESS=0x70000000 +SBL_DEV_ID=55 + +MULTI_CORE_IMAGE_GEN = $(CCS_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/multicoreImageGen/multicoreImageGen.js +OUTRPRC_CMD = $(CCS_NODE) $(MCU_PLUS_SDK_PATH)/tools/boot/out2rprc/elf2rprc.js +APP_IMAGE_SIGN_CMD = $(MCU_PLUS_SDK_PATH)/source/security/security_common/tools/boot/signing/appimage_x509_cert_gen.py + +ifeq ($(OS),Windows_NT) + XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.exe +else + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S), Darwin) + XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.out.mac + else + XIPGEN_CMD=$(MCU_PLUS_SDK_PATH)/tools/boot/xipGen/xipGen.out +endif +endif + +MULTI_CORE_IMAGE_PARAMS = \ + $(BOOTIMAGE_RPRC_NAME)@$(BOOTIMAGE_CORE_ID_r5fss0-0) \ + +MULTI_CORE_IMAGE_PARAMS_XIP = \ + $(BOOTIMAGE_RPRC_NAME_XIP)@$(BOOTIMAGE_CORE_ID_r5fss0-0) \ + +all: +ifeq ($(CCS_IDE_MODE),cloud) +# No post build steps +else + @echo Boot image: am243x:r5fss0-0:freertos:ti-arm-clang $(BOOTIMAGE_NAME) ... + $(OUTRPRC_CMD) $(OUTFILE) >> $(BOOTIMAGE_TEMP_OUT_FILE) + $(COPY) $(OUTNAME).rprc $(BOOTIMAGE_RPRC_NAME) + $(COPY) $(BOOTIMAGE_RPRC_NAME) $(BOOTIMAGE_RPRC_NAME_TMP) + $(RM) $(BOOTIMAGE_RPRC_NAME) + $(XIPGEN_CMD) -i $(BOOTIMAGE_RPRC_NAME_TMP) -o $(BOOTIMAGE_RPRC_NAME) -x $(BOOTIMAGE_RPRC_NAME_XIP) --flash-start-addr 0x60000000 -v > $(BOOTIMAGE_TEMP_OUT_FILE) + $(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME) $(MULTI_CORE_IMAGE_PARAMS) >> $(BOOTIMAGE_TEMP_OUT_FILE) + $(MULTI_CORE_IMAGE_GEN) --devID $(SBL_DEV_ID) --out $(BOOTIMAGE_NAME_XIP) $(MULTI_CORE_IMAGE_PARAMS_XIP) >> $(BOOTIMAGE_TEMP_OUT_FILE) +ifeq ($(DEVICE_TYPE),HS) +# Sign the appimage using appimage signing script +ifeq ($(ENC_ENABLED),no) + @echo Boot image signing: Encryption is disabled. + $(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 1 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME).hs + $(RM) $(BOOTIMAGE_NAME) +else + @echo Boot image signing: Encryption is enabled. + $(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 1 --key $(APP_SIGNING_KEY) --enc y --enckey $(APP_ENCRYPTION_KEY) --output $(BOOTIMAGE_NAME).hs + $(RM) $(BOOTIMAGE_NAME)-enc +endif +else +# Sign the appimage for HS-FS using appimage signing script + $(PYTHON) $(APP_IMAGE_SIGN_CMD) --bin $(BOOTIMAGE_NAME) --authtype 1 --key $(APP_SIGNING_KEY) --output $(BOOTIMAGE_NAME).hs_fs + $(RM) $(BOOTIMAGE_NAME) +endif + $(RM) $(BOOTIMAGE_RPRC_NAME_TMP) +ifeq ($(DEVICE_TYPE),HS) + @echo Boot image: am243x:r5fss0-0:freertos:ti-arm-clang $(BOOTIMAGE_NAME).hs Done !!! + @echo . +else + @echo Boot image: am243x:r5fss0-0:freertos:ti-arm-clang $(BOOTIMAGE_NAME).hs_fs Done !!! + @echo . +endif +endif diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_projectspec b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_projectspec new file mode 100644 index 0000000..136afd4 --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_projectspec @@ -0,0 +1,16 @@ +export PRU_NO_CODE_TOOL_PATH?=$(abspath ../../../../../..) +include $(PRU_NO_CODE_TOOL_PATH)/imports.mak + +PROFILE?=Release + +PROJECT_NAME=tamagawa_single_channel_am243x-lp_r5fss0-0_freertos_ti-arm-clang + +all: + $(CCS_ECLIPSE) -noSplash -data $(PRU_NO_CODE_TOOL_PATH)/ccs_projects -application com.ti.ccstudio.apps.projectBuild -ccs.projects $(PROJECT_NAME) -ccs.configuration $(PROFILE) + +clean: + $(CCS_ECLIPSE) -noSplash -data $(PRU_NO_CODE_TOOL_PATH)/ccs_projects -application com.ti.ccstudio.apps.projectBuild -ccs.projects $(PROJECT_NAME) -ccs.configuration $(PROFILE) -ccs.clean + +export: + $(MKDIR) $(PRU_NO_CODE_TOOL_PATH)/ccs_projects + $(CCS_ECLIPSE) -noSplash -data $(PRU_NO_CODE_TOOL_PATH)/ccs_projects -application com.ti.ccstudio.apps.projectCreate -ccs.projectSpec example.projectspec -ccs.overwrite full diff --git a/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/syscfg_c.rov.xs b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/syscfg_c.rov.xs new file mode 100644 index 0000000..f716f8e --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/syscfg_c.rov.xs @@ -0,0 +1,12 @@ +/* + * ======== syscfg_c.rov.xs ======== + * This file contains the information needed by the Runtime Object + * View (ROV) tool. + */ +var crovFiles = [ + "kernel/freertos/rov/FreeRTOS.rov.js", +]; + +var objectViewerFiles = [ + "kernel/freertos/rov_theia/FreeRTOS_Theia.rov.js", +]; diff --git a/examples/tamagawa_single_channel/mcuplus/empty_example.c b/examples/tamagawa_single_channel/mcuplus/empty_example.c new file mode 100644 index 0000000..f87a062 --- /dev/null +++ b/examples/tamagawa_single_channel/mcuplus/empty_example.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024-2025 Texas Instruments Incorporated + * + * 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 Texas Instruments Incorporated 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include "ti_drivers_config.h" +#include "ti_drivers_open_close.h" +#include "ti_board_open_close.h" +#include + +#include + +/* + * IPC shared memory layout (PRU1 SHAREDMEM physical base = 0x30010000): + * offset 0x00 : uint8_t trigger (R5F writes 1; PRU clears to 0 when done) + * offset 0x01 : uint8_t command (R5F writes pre-encoded TX byte before trigger) + * offset 0x02 : uint8_t reserved[2] + * offset 0x04 : uint32_t result_lo (PRU stores R1 here — bits [31:0] of raw RX) + * offset 0x08 : uint32_t result_hi (PRU stores R2 here — bits [39:32] of raw RX) + * + * Command byte values (raw CF byte, framing handled by no-code TX Op block): + * 0x92 (146) -> DATA_ID_2 : Encoder ID — 4 RX frames (40 wire bits) + * 0x02 (2) -> DATA_ID_0 : Absolute position — 6 RX frames (60 wire bits) + * 0x8A (138) -> DATA_ID_1 : Multi-turn data — 6 RX frames (60 wire bits) + */ +#define PRU1_DRAM_BASE (0x30010000U) +#define IPC_TRIGGER_OFFSET (0x00U) +#define IPC_COMMAND_OFFSET (0x01U) +#define IPC_RESULT_OFFSET (0x04U) + +#define CMD_DATA_ID_2 (0x92U) /* Encoder ID — 4 RX frames */ +#define CMD_DATA_ID_0 (0x02U) /* Absolute position — 6 RX frames */ +#define CMD_DATA_ID_1 (0x8AU) /* Multi-turn data — 6 RX frames */ + +#define IPC_POLL_TIMEOUT_US (5000U) +#define IPC_POLL_SLEEP_US (10U) + +PRUICSS_Handle gPruIcss0Handle; + +void empty_example_main(void *args) +{ + int status; + uint32_t input; + volatile uint8_t *pTrigger = (volatile uint8_t *)(PRU1_DRAM_BASE + IPC_TRIGGER_OFFSET); + volatile uint8_t *pCommand = (volatile uint8_t *)(PRU1_DRAM_BASE + IPC_COMMAND_OFFSET); + volatile uint32_t *pResult = (volatile uint32_t *)(PRU1_DRAM_BASE + IPC_RESULT_OFFSET); + + Drivers_open(); + + status = Board_driversOpen(); + DebugP_assert(SystemP_SUCCESS == status); + + /* Enable encoder booster pack power (GPIO1_78 = MMC1_SDWP) */ + uint32_t gpioBaseAddr = (uint32_t)AddrTranslateP_getLocalAddr(TAMAGAWA0_BP_POWER_EN0_BASE_ADDR); + GPIO_setDirMode(gpioBaseAddr, TAMAGAWA0_BP_POWER_EN0_PIN, GPIO_DIRECTION_OUTPUT); + GPIO_pinWriteHigh(gpioBaseAddr, TAMAGAWA0_BP_POWER_EN0_PIN); + ClockP_usleep(1000); + + gPruIcss0Handle = PRUICSS_open(CONFIG_PRU_ICSS0); + + /* Route GPI9 to PERIF0_IN via SA_MX_REG (required for channel 0 RX) */ + status = PRUICSS_setSaMuxMode(gPruIcss0Handle, PRUICSS_SA_MUX_MODE_SD_ENDAT); + DebugP_assert(SystemP_SUCCESS == status); + + status = PRUICSS_initMemory(gPruIcss0Handle, PRUICSS_DATARAM(PRUICSS_PRU0)); + DebugP_assert(status != 0); + status = PRUICSS_initMemory(gPruIcss0Handle, PRUICSS_DATARAM(PRUICSS_PRU1)); + DebugP_assert(status != 0); + volatile uint8_t *pSmem = (volatile uint8_t *)PRU1_DRAM_BASE; + pSmem[IPC_TRIGGER_OFFSET] = 0U; + pSmem[IPC_RESULT_OFFSET] = 0U; + pSmem[IPC_RESULT_OFFSET+1] = 0U; + pSmem[IPC_RESULT_OFFSET+2] = 0U; + pSmem[IPC_RESULT_OFFSET+3] = 0U; + status = PRUICSS_loadFirmware(gPruIcss0Handle, PRUICSS_PRU1, PRU1Firmware_0, sizeof(PRU1Firmware_0)); + DebugP_assert(SystemP_SUCCESS == status); + DebugP_log("Loaded encoder firmware.\r\n"); + + DebugP_log("Commands:\r\n"); + DebugP_log(" 0 -> DATA_ID_0 : Absolute position (CF, SF, ABS[23:0], CRC)\r\n"); + DebugP_log(" 1 -> DATA_ID_1 : Multi-turn data (CF, SF, ABM[23:0], CRC)\r\n"); + DebugP_log(" 2 -> DATA_ID_2 : Encoder ID readout (CF, SF, ENID, CRC)\r\n"); + + while (1) + { + DebugP_log("\r\nEnter command (0, 1 or 2): "); + DebugP_scanf("%d", &input); + + if (input > 2) + { + DebugP_log("Invalid input, enter 0, 1 or 2.\r\n"); + continue; + } + + /* Write command byte then trigger PRU */ + *pCommand = (input == 0) ? CMD_DATA_ID_0 : + (input == 1) ? CMD_DATA_ID_1 : CMD_DATA_ID_2; + *pTrigger = 1U; + + /* Poll until PRU clears trigger (transaction done) */ + uint32_t elapsed = 0U; + while (*pTrigger != 0U) + { + ClockP_usleep(IPC_POLL_SLEEP_US); + elapsed += IPC_POLL_SLEEP_US; + if (elapsed >= IPC_POLL_TIMEOUT_US) + { + DebugP_log("ERROR: PRU timeout.\r\n"); + break; + } + } + + if (*pTrigger != 0U) + { + continue; + } + + /* + * Raw bitstream layout (bit 0 = first data bit received, 10-bit frames): + * Each Tamagawa frame occupies 10 bits: 8 data bits + stop + start of next frame. + * Frame N data bits: raw >> (N * 10) & 0xFF + * + * DATA_ID_2 (4 frames): CF | SF | ENID | CRC + * bits [7:0] = CF + * bits [17:10] = SF + * bits [27:20] = ENID + * bits [37:30] = CRC + * + * DATA_ID_0 (6 frames): CF | SF | ABS[7:0] | ABS[15:8] | ABS[23:16] | CRC + * bits [7:0] = CF + * bits [17:10] = SF + * bits [27:20] = ABS[7:0] (lower byte) + * bits [37:30] = ABS[15:8] (middle byte) + * bits [47:40] = ABS[23:16] (upper byte) + * bits [57:50] = CRC + */ + uint64_t raw = ((uint64_t)pResult[1] << 32) | pResult[0]; + + uint8_t cf = raw & 0xFF; + uint8_t sf = (raw >> 10) & 0xFF; + + if (input == 0) + { + /* DATA_ID_0: Absolute position */ + uint8_t abs_lo = (raw >> 20) & 0xFF; + uint8_t abs_mid = (raw >> 30) & 0xFF; + uint8_t abs_hi = (raw >> 40) & 0xFF; + uint8_t crc = (raw >> 50) & 0xFF; + uint32_t abs_pos = ((uint32_t)abs_hi << 16) | ((uint32_t)abs_mid << 8) | abs_lo; + DebugP_log("DATA_ID_0 CF=0x%02X SF=0x%02X ABS=0x%06X CRC=0x%02X\r\n", + cf, sf, abs_pos, crc); + } + else if (input == 1) + { + /* DATA_ID_1: Multi-turn data */ + uint8_t abm_lo = (raw >> 20) & 0xFF; + uint8_t abm_mid = (raw >> 30) & 0xFF; + uint8_t abm_hi = (raw >> 40) & 0xFF; + uint8_t crc = (raw >> 50) & 0xFF; + uint32_t abm = ((uint32_t)abm_hi << 16) | ((uint32_t)abm_mid << 8) | abm_lo; + DebugP_log("DATA_ID_1 CF=0x%02X SF=0x%02X ABM=0x%06X CRC=0x%02X\r\n", + cf, sf, abm, crc); + } + else + { + /* DATA_ID_2: Encoder ID */ + uint8_t enid = (raw >> 20) & 0xFF; + uint8_t crc = (raw >> 30) & 0xFF; + DebugP_log("DATA_ID_2 CF=0x%02X SF=0x%02X ENID=0x%02X CRC=0x%02X\r\n", + cf, sf, enid, crc); + } + } + + Board_driversClose(); + Drivers_close(); +} diff --git a/examples/tamagawa_single_channel/readme.md b/examples/tamagawa_single_channel/readme.md new file mode 100644 index 0000000..2b3bd87 --- /dev/null +++ b/examples/tamagawa_single_channel/readme.md @@ -0,0 +1,274 @@ +# Tamagawa Single Channel + +## Introduction + +This example demonstrates how to use the PRU No-Code Tool to communicate with a Tamagawa encoder over the ICSSG 3-Channel Peripheral Interface. The R5F writes a command byte and a trigger to shared memory. The PRU reads the command byte, transmits it to the encoder, receives the response, stores the raw RX bitstream in shared memory, and clears the trigger. The R5F then decodes the response based on which command was sent. + +Three commands are supported: + +| User Input | DATA_ID | Command byte (CF) | Description | RX frames | +|------------|---------|-------------------|-------------|-----------| +| `0` | DATA_ID_0 | `0x02` | Absolute position (ABS) | 6 | +| `1` | DATA_ID_1 | `0x8A` | Multi-turn data (ABM) | 6 | +| `2` | DATA_ID_2 | `0x92` | Encoder ID (ENID) | 4 | + +The logic analyzer snapshot below shows the encoder command and response: + +
+tamagawa_single_channel +
Tamagawa DATA_ID_2 transaction — white : PERIF0_CLK, brown : Tx data (0x92), red : Encoder Response, yellow : PERIF0_OUT_EN
+
+ +--- + +## Hardware Setup + +The Tamagawa booster pack is connected to the AM243x-LP. The encoder power enable pin (GPIO1_78, MMC1_SDWP, J5.C16) must be driven HIGH by the R5F before loading PRU firmware. The encoder RX line is routed to PRU1 channel 0 via the SA_MUX (GPI13 → PERIF0_IN). + +| Signal | AM243x-LP Pin | Notes | +|----------------|---------------|--------------------------------| +| PERIF0_CLK | PRG0_PRU1_GPO0 (L5) | Encoder clock output | +| PERIF0_OUT | PRG0_PRU1_GPO1 (J2) | TX to encoder | +| PERIF0_OUT_EN | PRG0_PRU1_GPO2 (M2) | TX enable | +| PERIF0_IN | PRG0_PRU1_GPI13 (T4)| RX from encoder (via SA_MUX)| +| Encoder Power | MMC1_SDWP (C16) | GPIO1_78, drive HIGH | + +--- + +## Tamagawa Protocol Background + +Tamagawa is a synchronous serial encoder protocol using a 3-wire interface (CLK, TX, RX). The ICSSG 3-Channel Peripheral Interface handles the clocking and framing autonomously once configured. + +Each wire frame is **10 bits**: `start(0) + data[b0..b7] + stop(1)`, transmitted LSB first. + +**Supported commands:** + +| DATA_ID | CF byte | TX frames | RX frames | RX fields | +|---------|---------|-----------|-----------|-----------| +| DATA_ID_0 | `0x02` | 1 | 6 | CF, SF, ABS[23:0], CRC | +| DATA_ID_1 | `0x8A` | 1 | 6 | CF, SF, ABM[23:0], CRC | +| DATA_ID_2 | `0x92` | 1 | 4 | CF, SF, ENID, CRC | + +--- + +## No-Code Tool Block Design + +The entire IPC loop — trigger polling, command read, UART config, TX, RX, result storage, and trigger clear — is implemented using no-code blocks. No hand-written assembly is needed. + +The command byte is not hardcoded in the PRU. The R5F writes the desired CF byte into shared memory at offset `0x01` before setting the trigger. The PRU reads it with a `Memory Read` block and passes it directly to the `UART TX Op`. + +### Block Flow + +``` +[Loop: repeat_always] + │ + ├─► [Memory Read: trigger] read trigger byte from shared memory offset 0x00 + │ │ + │ [Load Constant: trigger_true] load value 1 + │ │ + ├─► [If/Else: trigger == 1?] + │ │ + │ TRUE branch: + │ ├─► [UART Config: config] configure peripheral (baud, frame size, clk mode) + │ ├─► [Memory Read: read_command] read command byte from shared memory offset 0x01 + │ ├─► [UART TX Op: tx] transmit command byte to encoder (1 frame) + │ ├─► [UART RX Op: rx_encoder] receive encoder response (up to 62 wire bits) + │ ├─► [Memory Write: write_response] store R1:R2 (64 bits) at shared memory offset 0x04 + │ ├─► [Load Constant: trigger_clear_value] load 0 + │ └─► [Memory Write: clear_trigger] clear trigger byte at offset 0x00 + │ + └─► [Delay: nop] (FALSE branch — yield one cycle before polling again) +``` + +### Block Configuration Details + +**Memory Variable (`Memory_Variable_0`)** +- Size: 16 bytes +- Location: `smem` (PRU1 DRAM, physical base `0x30010000`) +- Layout: + ``` + offset 0x00 : uint8_t trigger (R5F writes 1; PRU clears to 0 when done) + offset 0x01 : uint8_t command (R5F writes CF byte before trigger) + offset 0x02 : uint8_t reserved[2] + offset 0x04 : uint32_t result_lo (R1 — raw RX bits [31:0]) + offset 0x08 : uint32_t result_hi (R2 — raw RX bits [39:32]) + ``` + +**Memory Read (`trigger`)** +- Operation: read +- Offset: 0x00, size: 1 byte +- Reads the trigger byte set by R5F into `R0.b0` + +**Load Constant (`trigger_true`)** +- Value: 1 +- Used as the comparator input to the If/Else block + +**If/Else (`If_Else_0`)** +- Condition: `input1 == input2` (trigger value == 1) +- TRUE → runs config + command read + TX + RX + store + clear +- FALSE → falls through to Delay (nop), then loops + +**UART Config (`config`)** +- TX channel: 0, baud rate: 2.5 Mbps, clock source: 200 MHz +- TX frame: 8 data bits, start bit = 0 (low), stop bit = 1 (high), LSB first +- RX channel: 0, baud rate: 2.5 Mbps, 8x oversample +- RX frame size: 62 bits (covers up to 6 Tamagawa wire frames + margin) +- Clock mode: 1 (free-running, stops high after last RX frame — keeps clock running through encoder response) +- Configures GPCFG1 for peripheral mux mode on PRU1, sets TXCFG, RXCFG, CH0_CFG0 + +**Memory Read (`read_command`)** +- Operation: read +- Offset: 0x01, size: 1 byte +- Reads the CF byte written by the R5F into `R0.b0`, which is then fed directly into the UART TX Op + +**UART TX Op (`tx`)** +- Input: `read_command` output (CF byte in R0.b0) +- Channel: 0, data bits: 8 +- Performs per-command reinit (clears FIFO and state machines), loads 2 bytes to FIFO, triggers TX, waits for TX complete + +**UART RX Op (`rx_encoder`)** +- Output registers: R1 (bits [31:0]), R2 (bits [39:32]) +- Channel: 0, frame size: 62 bits +- Enables rx_en, polls valid flag per bit (8x oversampled), accumulates all bits LSB-first into R1:R2 as a raw bitstream, then right-shifts to align and strips the first start bit + +**Memory Write (`write_response`)** +- Operation: write +- Offset: 0x04, size: 8 bytes +- Stores R1:R2 (64-bit result) to shared memory + +**Load Constant (`trigger_clear_value`)** +- Value: 0 + +**Memory Write (`clear_trigger`)** +- Operation: write +- Offset: 0x00, size: 1 byte +- Writes 0 to trigger byte — signals R5F that transaction is complete + +--- + +## Generated Assembly + +The no-code blocks generate the following PRU assembly (`pru_syscfg.asm`): + +```asm +sysconfig_generated_start: +startloop_0: + m_memory_load R0.b0, buffer, 0, 1 ; read trigger byte + LDI R0.b1, 1 ; load trigger_true = 1 + QBNE If_Else_0_FALSE, R0.b1, R0.b0 ; if trigger != 1, skip + m_uart_config_pru1_tx_ch0_lsb_start0_ss_rx_ch0_lsb_8x 0, 79, 1, 8, 0, 9, 1, 7, 0, 62 + m_memory_load R0.b0, buffer, 1, 1 ; read command byte from smem offset 0x01 + m_uart_tx_op_2byte_pru1_lsb_start0 R0.b0, 0, 8 + m_uart_rx_op_pru1_lsb_8x_ext R1, R2, 0, 9, 1, 7, 0, 62, 1 + m_memory_store_64 R1, R2, buffer, 4, 8 ; store result at offset 0x04 + LDI R0.b1, 0 + m_memory_store R0.b1, buffer, 0, 1 ; clear trigger +If_Else_0_FALSE: + nop + QBA startloop_0 +``` + +--- + +## Decoding the RX Result on R5F + +The RX macro collects all 62 wire bits as a raw bitstream (LSB first), strips the first start bit, and stores the result in R1:R2. Each Tamagawa frame occupies 10 bits in the stream (8 data bits + stop + start of next frame): + +``` +Raw bitstream layout (bit 0 = first data bit received): + +bits [7:0] = CF (command field echo) +bit [8] = stop bit +bit [9] = start bit of next frame +bits [17:10] = SF (status frame) +bits [27:20] = frame 2 data byte +bits [37:30] = frame 3 data byte +bits [47:40] = frame 4 data byte (6-frame commands only) +bits [57:50] = frame 5 data byte (6-frame commands only) +``` + +The general rule for any Tamagawa frame N (0-indexed): +```c +uint8_t frame_N_data = (raw >> (N * 10)) & 0xFF; +``` + +**DATA_ID_0 (6 frames) — Absolute position:** +``` +bits [7:0] = CF +bits [17:10] = SF +bits [27:20] = ABS[7:0] (low byte) +bits [37:30] = ABS[15:8] (mid byte) +bits [47:40] = ABS[23:16] (high byte) +bits [57:50] = CRC +``` + +**DATA_ID_1 (6 frames) — Multi-turn data:** +``` +bits [7:0] = CF +bits [17:10] = SF +bits [27:20] = ABM[7:0] (low byte) +bits [37:30] = ABM[15:8] (mid byte) +bits [47:40] = ABM[23:16] (high byte) +bits [57:50] = CRC +``` + +**DATA_ID_2 (4 frames) — Encoder ID:** +``` +bits [7:0] = CF +bits [17:10] = SF +bits [27:20] = ENID +bits [37:30] = CRC +``` + +R5F decoding (from `empty_example.c`): + +```c +uint64_t raw = ((uint64_t)pResult[1] << 32) | pResult[0]; + +uint8_t cf = raw & 0xFF; // bits [7:0] +uint8_t sf = (raw >> 10) & 0xFF; // bits [17:10] + +// DATA_ID_0: Absolute position +uint32_t abs_pos = ((uint32_t)((raw >> 40) & 0xFF) << 16) | + ((uint32_t)((raw >> 30) & 0xFF) << 8) | + ((raw >> 20) & 0xFF); +uint8_t crc = (raw >> 50) & 0xFF; + +// DATA_ID_1: Multi-turn data +uint32_t abm = ((uint32_t)((raw >> 40) & 0xFF) << 16) | + ((uint32_t)((raw >> 30) & 0xFF) << 8) | + ((raw >> 20) & 0xFF); +uint8_t crc = (raw >> 50) & 0xFF; + +// DATA_ID_2: Encoder ID +uint8_t enid = (raw >> 20) & 0xFF; +uint8_t crc = (raw >> 30) & 0xFF; +``` + +--- + +# Supported Combinations + + Parameter | Value + ---------------|----------- + ICSSG | ICSSG0 - PRU1 + Toolchain | pru-cgt + Board | am243x-lp + Example folder | examples/tamagawa_single_channel/ + +# Steps to Run the Example + +> Prerequisite: [PRU-CGT-2-3](https://www.ti.com/tool/PRU-CGT) (ti-pru-cgt) should be installed at: `C:/ti/` + +- **When using CCS projects to build**, import the CCS project from the above mentioned Example folder path for R5F and PRU. After this `main.asm`, `linker.cmd` files get copied to the CCS workspace of the PRU project. + - For hardware setup with the am243x-lp and the Tamagawa encoder, refer [this page](https://software-dl.ti.com/processor-industrial-sw/esd/motor_control_sdk/am243x/latest/docs/api_guide_am243x/EXAMPLE_MOTORCONTROL_TAMAGAWA.html) + + - Build the PRU project using the CCS project menu (see [for AM243x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/latest/exports/docs/api_guide_am243x/CCS_PROJECTS_PAGE.html)). + - Build Flow: Once you click on build in the PRU project, the firmware header file generated in the release or debug folder of the CCS workspace is moved to `` + + - Build the R5F project using the CCS project menu (see [for AM243x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/latest/exports/docs/api_guide_am243x/CCS_PROJECTS_PAGE.html)). + - The firmware header file path is included in the R5F project include options by default. Instructions in the firmware header file can be written into PRU IRAM memory using the `PRUICSS_loadFirmware` API (see [for AM243x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/latest/exports/docs/api_guide_am243x/group__DRV__PRUICSS__MODULE.html#ga3e7c763e5343fe98f7011f388a0b7ffe)). + + - Launch a CCS debug session and run the executable (see [for AM243x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/latest/exports/docs/api_guide_am243x/CCS_LAUNCH_PAGE.html)). + + - Open a serial terminal (115200 8N1) on the UART backchannel port. Enter `0`, `1`, or `2` to select the command. The terminal will print the decoded fields for the selected DATA_ID. diff --git a/examples/uart_crc/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg b/examples/uart_crc/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg index 25e7190..1c57fcf 100644 --- a/examples/uart_crc/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg +++ b/examples/uart_crc/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg @@ -9,16 +9,18 @@ /** * Import the modules used in this configuration. */ -const crc_block = scripting.addModule("/pru_blocks/application_specific/crc_block", {}, false); -const crc_block1 = crc_block.addInstance(); -const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); +const crc_block = scripting.addModule("/pru_blocks/application_specific/crc_block", {}, false); +const crc_block1 = crc_block.addInstance(); +const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); const load_constant_block1 = load_constant_block.addInstance(); const load_constant_block2 = load_constant_block.addInstance(); -const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); -const flow_control_block1 = flow_control_block.addInstance(); -const uart_tx = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx", {}, false); -const uart_tx1 = uart_tx.addInstance(); -const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); +const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); +const flow_control_block1 = flow_control_block.addInstance(); +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +const uart_tx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx_op", {}, false); +const uart_tx_op1 = uart_tx_op.addInstance(); +const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); const memory_access_block1 = memory_access_block.addInstance(); const memory_access_block2 = memory_access_block.addInstance(); const memory_access_block3 = memory_access_block.addInstance(); @@ -37,30 +39,36 @@ load_constant_block2.$name = "Load_Constant_1"; flow_control_block1.$name = "Flow_Control_0"; -uart_tx1.$name = "PRU_UART_TX_0"; -uart_tx1.dataBits = 16; +uart_tx_op1.$name = "PRU_UART_TX_OP_0"; +uart_tx_op1.dataBits = 16; -// memory_access_block1 owns the auto-created Memory Variable (buffer, 2 bytes) -memory_access_block1.$name = "Memory_Access_0"; -memory_access_block1.operationMode = "write"; -memory_access_block1.memVar.labelName = "buffer"; -memory_access_block1.memVar.sizeInBytes = 2; -memory_access_block1.dataSize = 1; +uart_config1.$name = "PRU_UART_CONFIG_0"; +uart_tx_op1.uartConfig = uart_config1; +uart_config1.enableRX = false; -// remaining access blocks reference the same buffer by label name -memory_access_block2.$name = "Memory_Access_1"; -memory_access_block2.symbolSelect = "buffer"; -memory_access_block2.dataSize = 1; +memory_access_block1.$name = "Memory_Access_0"; +memory_access_block1.operationMode = "write"; +memory_access_block1.dataSize = 1; + +memory_access_block2.$name = "Memory_Access_1"; +memory_access_block2.dataSize = 1; memory_access_block3.$name = "Memory_Access_2"; memory_access_block3.operationMode = "write"; -memory_access_block3.symbolSelect = "buffer"; memory_access_block3.offsetValue = 0x1; memory_access_block3.dataSize = 1; -memory_access_block4.$name = "Memory_Access_3"; -memory_access_block4.symbolSelect = "buffer"; -memory_access_block4.dataSize = 2; +memory_access_block4.$name = "Memory_Access_3"; +memory_access_block4.dataSize = 2; + +const memory_variable_block = scripting.addModule("/pru_blocks/utils/memory_variable_block", {}, false); +const memory_variable_block1 = memory_variable_block.addInstance({}, false); +memory_variable_block1.$name = "Memory_Variable_0"; +memory_access_block1.memVar = memory_variable_block1; +memory_access_block2.memVar = memory_variable_block1; +memory_access_block3.memVar = memory_variable_block1; +memory_access_block4.memVar = memory_variable_block1; +memory_variable_block1.sizeInBytes = 2; /** * Connections between modules. @@ -71,20 +79,25 @@ scripting.connect(crc_block1, "prev", load_constant_block2, "next"); scripting.connect(crc_block1, "next", memory_access_block3, "prev"); scripting.connect(crc_block1, "output1", memory_access_block3, "input1"); scripting.connect(load_constant_block1, "output1", memory_access_block1, "input1"); -scripting.connect(flow_control_block1, "prev", uart_tx1, "next"); -scripting.connect(uart_tx1, "input1", memory_access_block4, "output1"); +scripting.connect(load_constant_block1, "next", memory_access_block1, "prev"); +scripting.connect(flow_control_block1, "prev", uart_tx_op1, "next"); +scripting.connect(uart_tx_op1, "input1", memory_access_block4, "output1"); +scripting.connect(uart_tx_op1, "prev", memory_access_block4, "next"); +scripting.connect(uart_config1, "prev", memory_access_block3, "next"); +scripting.connect(uart_config1, "next", memory_access_block4, "prev"); scripting.connect(memory_access_block1, "next", memory_access_block2, "prev"); -scripting.connect(memory_access_block3, "next", memory_access_block4, "prev"); /** * (x,y) coordinates for modules that are displayed in a graph. */ -crc_block1.$position = [550,140]; -load_constant_block1.$position = [20,160]; -load_constant_block2.$position = [370,90]; -flow_control_block1.$position = [1250,130]; -uart_tx1.$position = [1075,130]; -memory_access_block1.$position = [195,160]; -memory_access_block2.$position = [370,160]; -memory_access_block3.$position = [725,140]; -memory_access_block4.$position = [900,140]; +crc_block1.$position = [550,140]; +load_constant_block1.$position = [20,160]; +load_constant_block2.$position = [370,90]; +flow_control_block1.$position = [1250,130]; +uart_tx_op1.$position = [1090,120]; +uart_config1.$position = [840,35]; +memory_access_block1.$position = [195,160]; +memory_access_block2.$position = [370,160]; +memory_access_block3.$position = [725,140]; +memory_access_block4.$position = [965,125]; +memory_variable_block1.$position = [-180,210]; diff --git a/examples/uart_crc/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg b/examples/uart_crc/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg index 7494c0b..1bd7d2c 100644 --- a/examples/uart_crc/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg +++ b/examples/uart_crc/firmware/am243x-lp/icss_g0_pru1_fw/example.syscfg @@ -9,15 +9,17 @@ /** * Import the modules used in this configuration. */ -const crc_block = scripting.addModule("/pru_blocks/application_specific/crc_block", {}, false); -const crc_block1 = crc_block.addInstance(); -const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); +const crc_block = scripting.addModule("/pru_blocks/application_specific/crc_block", {}, false); +const crc_block1 = crc_block.addInstance(); +const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); const load_constant_block1 = load_constant_block.addInstance(); -const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); -const flow_control_block1 = flow_control_block.addInstance(); -const uart_rx = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx", {}, false); -const uart_rx1 = uart_rx.addInstance(); -const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); +const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); +const flow_control_block1 = flow_control_block.addInstance(); +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +const uart_rx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx_op", {}, false); +const uart_rx_op1 = uart_rx_op.addInstance(); +const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); const memory_access_block1 = memory_access_block.addInstance(); const memory_access_block2 = memory_access_block.addInstance(); @@ -31,20 +33,26 @@ load_constant_block1.$name = "Load_Constant_0"; flow_control_block1.$name = "Flow_Control_0"; -uart_rx1.$name = "PRU_UART_RX_0"; -uart_rx1.frameSize = 18; +uart_rx_op1.$name = "PRU_UART_RX_OP_0"; +uart_rx_op1.rxFrameSize = 18; -// memory_access_block1 owns the auto-created Memory Variable (buffer, 4 bytes) -memory_access_block1.$name = "Memory_Access_0"; -memory_access_block1.operationMode = "write"; -memory_access_block1.memVar.labelName = "buffer"; -memory_access_block1.memVar.sizeInBytes = 4; -memory_access_block1.dataSize = 2; +uart_config1.$name = "PRU_UART_CONFIG_0"; +uart_rx_op1.uartConfig = uart_config1; +uart_config1.enableTX = false; -// memory_access_block2 references the same buffer by label name -memory_access_block2.$name = "Memory_Access_1"; -memory_access_block2.symbolSelect = "buffer"; -memory_access_block2.dataSize = 1; +memory_access_block1.$name = "Memory_Access_0"; +memory_access_block1.operationMode = "write"; +memory_access_block1.dataSize = 2; + +memory_access_block2.$name = "Memory_Access_1"; +memory_access_block2.dataSize = 1; + +const memory_variable_block = scripting.addModule("/pru_blocks/utils/memory_variable_block", {}, false); +const memory_variable_block1 = memory_variable_block.addInstance({}, false); +memory_variable_block1.$name = "Memory_Variable_0"; +memory_access_block1.memVar = memory_variable_block1; +memory_access_block2.memVar = memory_variable_block1; +memory_variable_block1.sizeInBytes = 4; /** * Connections between modules. @@ -53,16 +61,19 @@ scripting.connect(crc_block1, "input1", load_constant_block1, "output1"); scripting.connect(crc_block1, "input2", memory_access_block2, "output1"); scripting.connect(crc_block1, "prev", memory_access_block2, "next"); scripting.connect(crc_block1, "next", flow_control_block1, "prev"); -scripting.connect(uart_rx1, "output1", memory_access_block1, "input1"); -scripting.connect(uart_rx1, "next", memory_access_block1, "prev"); +scripting.connect(uart_rx_op1, "output1", memory_access_block1, "input1"); +scripting.connect(uart_rx_op1, "prev", uart_config1, "next"); +scripting.connect(uart_rx_op1, "next", memory_access_block1, "prev"); scripting.connect(memory_access_block1, "next", memory_access_block2, "prev"); /** * (x,y) coordinates for modules that are displayed in a graph. */ -crc_block1.$position = [140,-80]; -load_constant_block1.$position = [-10,-130]; -flow_control_block1.$position = [285,-90]; -uart_rx1.$position = [-315,-75]; -memory_access_block1.$position = [-150,-65]; -memory_access_block2.$position = [-10,-25]; +crc_block1.$position = [140,-80]; +load_constant_block1.$position = [-10,-130]; +flow_control_block1.$position = [285,-90]; +uart_rx_op1.$position = [-290,-80]; +uart_config1.$position = [-410,-85]; +memory_access_block1.$position = [-150,-65]; +memory_access_block2.$position = [-10,-25]; +memory_variable_block1.$position = [-385,-250]; diff --git a/examples/uart_crc/firmware/am261x-lp/icss_m1_pru0_fw/example.syscfg b/examples/uart_crc/firmware/am261x-lp/icss_m1_pru0_fw/example.syscfg index dc806ee..c7d9059 100644 --- a/examples/uart_crc/firmware/am261x-lp/icss_m1_pru0_fw/example.syscfg +++ b/examples/uart_crc/firmware/am261x-lp/icss_m1_pru0_fw/example.syscfg @@ -9,16 +9,18 @@ /** * Import the modules used in this configuration. */ -const crc_block = scripting.addModule("/pru_blocks/application_specific/crc_block", {}, false); -const crc_block1 = crc_block.addInstance(); -const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); +const crc_block = scripting.addModule("/pru_blocks/application_specific/crc_block", {}, false); +const crc_block1 = crc_block.addInstance(); +const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load_constant_block", {}, false); const load_constant_block1 = load_constant_block.addInstance(); const load_constant_block2 = load_constant_block.addInstance(); -const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); -const flow_control_block1 = flow_control_block.addInstance(); -const uart_tx = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx", {}, false); -const uart_tx1 = uart_tx.addInstance(); -const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); +const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); +const flow_control_block1 = flow_control_block.addInstance(); +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +const uart_tx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_tx_op", {}, false); +const uart_tx_op1 = uart_tx_op.addInstance(); +const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); const memory_access_block1 = memory_access_block.addInstance(); const memory_access_block2 = memory_access_block.addInstance(); const memory_access_block3 = memory_access_block.addInstance(); @@ -37,30 +39,36 @@ load_constant_block2.$name = "Load_Constant_1"; flow_control_block1.$name = "Flow_Control_0"; -uart_tx1.$name = "PRU_UART_TX_0"; -uart_tx1.dataBits = 16; +uart_tx_op1.$name = "PRU_UART_TX_OP_0"; +uart_tx_op1.dataBits = 16; -// memory_access_block1 owns the auto-created Memory Variable (buffer, 2 bytes) -memory_access_block1.$name = "Memory_Access_0"; -memory_access_block1.operationMode = "write"; -memory_access_block1.memVar.labelName = "buffer"; -memory_access_block1.memVar.sizeInBytes = 2; -memory_access_block1.dataSize = 1; +uart_config1.$name = "PRU_UART_CONFIG_0"; +uart_tx_op1.uartConfig = uart_config1; +uart_config1.enableRX = false; -// remaining access blocks reference the same buffer by label name -memory_access_block2.$name = "Memory_Access_1"; -memory_access_block2.symbolSelect = "buffer"; -memory_access_block2.dataSize = 1; +memory_access_block1.$name = "Memory_Access_0"; +memory_access_block1.operationMode = "write"; +memory_access_block1.dataSize = 1; + +memory_access_block2.$name = "Memory_Access_1"; +memory_access_block2.dataSize = 1; memory_access_block3.$name = "Memory_Access_2"; memory_access_block3.operationMode = "write"; -memory_access_block3.symbolSelect = "buffer"; memory_access_block3.offsetValue = 0x1; memory_access_block3.dataSize = 1; -memory_access_block4.$name = "Memory_Access_3"; -memory_access_block4.symbolSelect = "buffer"; -memory_access_block4.dataSize = 2; +memory_access_block4.$name = "Memory_Access_3"; +memory_access_block4.dataSize = 2; + +const memory_variable_block = scripting.addModule("/pru_blocks/utils/memory_variable_block", {}, false); +const memory_variable_block1 = memory_variable_block.addInstance({}, false); +memory_variable_block1.$name = "Memory_Variable_0"; +memory_access_block1.memVar = memory_variable_block1; +memory_access_block2.memVar = memory_variable_block1; +memory_access_block3.memVar = memory_variable_block1; +memory_access_block4.memVar = memory_variable_block1; +memory_variable_block1.sizeInBytes = 2; /** * Connections between modules. @@ -71,20 +79,25 @@ scripting.connect(crc_block1, "prev", load_constant_block2, "next"); scripting.connect(crc_block1, "next", memory_access_block3, "prev"); scripting.connect(crc_block1, "output1", memory_access_block3, "input1"); scripting.connect(load_constant_block1, "output1", memory_access_block1, "input1"); -scripting.connect(flow_control_block1, "prev", uart_tx1, "next"); -scripting.connect(uart_tx1, "input1", memory_access_block4, "output1"); +scripting.connect(load_constant_block1, "next", memory_access_block1, "prev"); +scripting.connect(flow_control_block1, "prev", uart_tx_op1, "next"); +scripting.connect(uart_tx_op1, "input1", memory_access_block4, "output1"); +scripting.connect(uart_tx_op1, "prev", memory_access_block4, "next"); +scripting.connect(uart_config1, "prev", memory_access_block3, "next"); +scripting.connect(uart_config1, "next", memory_access_block4, "prev"); scripting.connect(memory_access_block1, "next", memory_access_block2, "prev"); -scripting.connect(memory_access_block3, "next", memory_access_block4, "prev"); /** * (x,y) coordinates for modules that are displayed in a graph. */ -crc_block1.$position = [550,140]; -load_constant_block1.$position = [20,160]; -load_constant_block2.$position = [370,90]; -flow_control_block1.$position = [1250,130]; -uart_tx1.$position = [1075,130]; -memory_access_block1.$position = [195,160]; -memory_access_block2.$position = [370,160]; -memory_access_block3.$position = [725,140]; -memory_access_block4.$position = [900,140]; +crc_block1.$position = [550,140]; +load_constant_block1.$position = [20,160]; +load_constant_block2.$position = [370,90]; +flow_control_block1.$position = [1250,130]; +uart_tx_op1.$position = [1090,120]; +uart_config1.$position = [840,35]; +memory_access_block1.$position = [195,160]; +memory_access_block2.$position = [370,160]; +memory_access_block3.$position = [725,140]; +memory_access_block4.$position = [965,125]; +memory_variable_block1.$position = [-180,210]; diff --git a/examples/uart_crc/firmware/am261x-lp/icss_m1_pru1_fw/example.syscfg b/examples/uart_crc/firmware/am261x-lp/icss_m1_pru1_fw/example.syscfg index 2b07577..a42173c 100644 --- a/examples/uart_crc/firmware/am261x-lp/icss_m1_pru1_fw/example.syscfg +++ b/examples/uart_crc/firmware/am261x-lp/icss_m1_pru1_fw/example.syscfg @@ -15,8 +15,10 @@ const load_constant_block = scripting.addModule("/pru_blocks/data_handling/load const load_constant_block1 = load_constant_block.addInstance(); const flow_control_block = scripting.addModule("/pru_blocks/program_control/flow_control_block", {}, false); const flow_control_block1 = flow_control_block.addInstance(); -const uart_rx = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx", {}, false); -const uart_rx1 = uart_rx.addInstance(); +const uart_config = scripting.addModule("/pru_blocks/pru_io_blocks/uart_config", {}, false); +const uart_config1 = uart_config.addInstance(); +const uart_rx_op = scripting.addModule("/pru_blocks/pru_io_blocks/uart_rx_op", {}, false); +const uart_rx_op1 = uart_rx_op.addInstance(); const memory_access_block = scripting.addModule("/pru_blocks/utils/memory_access_block", {}, false); const memory_access_block1 = memory_access_block.addInstance(); const memory_access_block2 = memory_access_block.addInstance(); @@ -31,8 +33,12 @@ load_constant_block1.$name = "Load_Constant_0"; flow_control_block1.$name = "Flow_Control_0"; -uart_rx1.$name = "PRU_UART_RX_0"; -uart_rx1.frameSize = 18; +uart_rx_op1.$name = "PRU_UART_RX_OP_0"; +uart_rx_op1.rxFrameSize = 18; + +uart_config1.$name = "PRU_UART_CONFIG_0"; +uart_rx_op1.uartConfig = uart_config1; +uart_config1.enableTX = false; memory_access_block1.$name = "Memory_Access_0"; memory_access_block1.operationMode = "write"; @@ -55,8 +61,9 @@ scripting.connect(crc_block1, "input1", load_constant_block1, "output1"); scripting.connect(crc_block1, "input2", memory_access_block2, "output1"); scripting.connect(crc_block1, "prev", memory_access_block2, "next"); scripting.connect(crc_block1, "next", flow_control_block1, "prev"); -scripting.connect(uart_rx1, "output1", memory_access_block1, "input1"); -scripting.connect(uart_rx1, "next", memory_access_block1, "prev"); +scripting.connect(uart_rx_op1, "output1", memory_access_block1, "input1"); +scripting.connect(uart_rx_op1, "prev", uart_config1, "next"); +scripting.connect(uart_rx_op1, "next", memory_access_block1, "prev"); scripting.connect(memory_access_block1, "next", memory_access_block2, "prev"); /** @@ -65,7 +72,8 @@ scripting.connect(memory_access_block1, "next", memory_access_block2, "prev"); crc_block1.$position = [140,-80]; load_constant_block1.$position = [-10,-130]; flow_control_block1.$position = [285,-90]; -uart_rx1.$position = [-315,-75]; +uart_rx_op1.$position = [-290,-80]; +uart_config1.$position = [-410,-85]; memory_access_block1.$position = [-150,-65]; memory_access_block2.$position = [-10,-25]; -memory_variable_block1.$position = [-320,-195]; +memory_variable_block1.$position = [-385,-250]; diff --git a/examples/uart_crc/firmware/main.asm b/examples/uart_crc/firmware/main.asm index dde02c7..5ab0d6e 100644 --- a/examples/uart_crc/firmware/main.asm +++ b/examples/uart_crc/firmware/main.asm @@ -43,7 +43,7 @@ sysconfig_generated_end: LDI32 R2, 0x48010000 .endif ; Store TX data sent at SMEM[4] using SBBO - SBBO &R0.b2, R2, 4, 1 ; Store R0.b0 at [smem + 0] + SBBO &R0.b1, R2, 4, 1 ; Store R0.b1 at [smem + 0] halt .elseif $isdefed("PRU1") zero &r0, 120 ; Clear the register space diff --git a/examples/uart_crc/readme.md b/examples/uart_crc/readme.md index db07ca7..0546bbf 100644 --- a/examples/uart_crc/readme.md +++ b/examples/uart_crc/readme.md @@ -7,10 +7,181 @@ This example demonstrates the use of pru-no-code-tool for sending a data byte an The data and its CRC are transmitted in a single UART frame whose snapshot from the logic analyzer is attached below :
-uart_crc +uart_crc
The data and its CRC is transmitted in a single frame (start bit 1 and stop bit 0) , white signal -> data+CRC, brown -> tx enable, red -> clock
+--- + +## No-Code Tool Block Design + +The entire TX pipeline (data store, CRC compute, CRC store, peripheral config, transmit) on PRU0 and the RX pipeline (receive, data extract, CRC recompute) on PRU1 are implemented using no-code blocks. No hand-written assembly handles the protocol logic — only the final SMEM write and CRC comparison at the end of `main.asm` are manual. + +The TX side uses the **UART Config + UART TX Op** block pair. The UART Config block handles one-time peripheral setup (baud rate, frame size, bit order), and the UART TX Op block handles the per-transmission work (frame construction, FIFO load, start trigger, wait for completion). This separation means the peripheral only needs to be configured once even if multiple TX operations were needed. + +### PRU0 — Transmitter Block Flow + +``` +[Load Constant: Load_Constant_0] value = 0xAA (data byte) + │ + ▼ +[Memory Write: Memory_Access_0] write 1 byte → buffer[0] (stores data byte in PRU DMEM) + │ + ▼ +[Memory Read: Memory_Access_1] read 1 byte ← buffer[0] (feeds data byte into CRC block) + │ ▲ + │ [Load Constant: Load_Constant_1] value = 0 (CRC init value) + │ │ + ▼ │ +[CRC: CRC_0] polynomial = 0x07 (CRC-8-ATM), input2 = data byte, input1 = init 0 + │ + ▼ +[Memory Write: Memory_Access_2] write 1 byte → buffer[1] (stores computed CRC) + │ + ▼ +[Memory Read: Memory_Access_3] read 2 bytes ← buffer[0] (reads data+CRC as a 16-bit word) + │ + ▼ +[UART Config: PRU_UART_CONFIG_0] configure peripheral (baud rate, frame size, bit order) + │ + ▼ +[UART TX Op: PRU_UART_TX_OP_0] transmit 16-bit frame (data byte in low byte, CRC in high byte) + │ + ▼ +[Flow Control: Flow_Control_0] HALT +``` + +### PRU1 — Receiver Block Flow + +``` +[UART Config: PRU_UART_CONFIG_0] configure peripheral (baud rate, oversample, frame size, bit order) + │ + ▼ +[UART RX Op: PRU_UART_RX_OP_0] receive 18-bit frame (start + 16 data bits + stop) + │ + ▼ +[Memory Write: Memory_Access_0] write 2 bytes → buffer[0] (stores raw received frame: data at [0], received CRC at [1]) + │ + ▼ +[Memory Read: Memory_Access_1] read 1 byte ← buffer[0] (extracts received data byte) + │ ▲ + │ [Load Constant: Load_Constant_0] value = 0 (CRC init value) + │ │ + ▼ │ +[CRC: CRC_0] polynomial = 0x07, input2 = received data byte, input1 = init 0 + │ + ▼ +[Flow Control: Flow_Control_0] HALT (CRC result in register, passed to main.asm for comparison) +``` + +### Block Configuration Details + +**PRU0 Blocks** + +**Memory Variable (`Memory_Variable_0`)** +- Size: 2 bytes, location: PRU DMEM +- Shared by all four Memory Access blocks as the named buffer `buffer` +- Layout: `buffer[0]` = data byte, `buffer[1]` = CRC byte + +**Load Constant (`Load_Constant_0`)** +- Value: 170 (0xAA) — the data byte to be transmitted + +**Memory Access (`Memory_Access_0`)** +- Operation: write, offset: 0x00, size: 1 byte +- Writes 0xAA to `buffer[0]` in PRU DMEM + +**Memory Access (`Memory_Access_1`)** +- Operation: read, offset: 0x00, size: 1 byte +- Reads `buffer[0]` back as the data input to the CRC block + +**Load Constant (`Load_Constant_1`)** +- Value: 0 — CRC initialisation value (no chaining, fresh CRC per frame) + +**CRC (`CRC_0`)** +- Polynomial: 0x07 (CRC-8-ATM / CCITT) +- input1: CRC init value (0) from `Load_Constant_1` +- input2: data byte from `Memory_Access_1` +- Computes an 8-bit CRC over the single data byte + +**Memory Access (`Memory_Access_2`)** +- Operation: write, offset: 0x01, size: 1 byte +- Writes the CRC result to `buffer[1]` — now `buffer` holds `[data | CRC]` + +**Memory Access (`Memory_Access_3`)** +- Operation: read, offset: 0x00, size: 2 bytes +- Reads the full 2-byte `[data | CRC]` word to feed the UART TX Op block + +**UART Config (`PRU_UART_CONFIG_0`)** +- TX only (RX disabled) +- Configures the ENDAT peripheral: baud rate, frame size, bit order, start/stop polarity +- Runs once before the TX Op — handles GPCFG, TXCFG, and CH_CFG0 register writes including `TX_FIFO_SWAP_BITS` for bit order + +**UART TX Op (`PRU_UART_TX_OP_0`)** +- Data bits: 16 +- Reads configuration from the paired `PRU_UART_CONFIG_0` block automatically +- Handles per-transmission work only: frame construction, FIFO load, start trigger, wait for TX complete +- Transmits `buffer[0:1]` as a single 16-bit UART frame (data in bits [7:0], CRC in bits [15:8]) + +--- + +**PRU1 Blocks** + +**Memory Variable (`Memory_Variable_0`)** +- Size: 4 bytes, location: PRU DMEM +- Shared by both Memory Access blocks as the named buffer `buffer` +- Layout: `buffer[0]` = received data byte, `buffer[1]` = received CRC byte + +**UART Config (`PRU_UART_CONFIG_0`)** +- RX only (TX disabled) +- Configures the ENDAT peripheral for reception: baud rate, oversample size, RX frame size, start bit polarity +- Runs once before the RX Op + +**UART RX Op (`PRU_UART_RX_OP_0`)** +- Frame size: 18 bits (start bit + 16 data bits + stop bit) +- Reads configuration from the paired `PRU_UART_CONFIG_0` block automatically +- Receives the full frame transmitted by PRU0 and outputs the 16-bit payload + +**Memory Access (`Memory_Access_0`)** +- Operation: write, offset: 0x00, size: 2 bytes +- Stores the received 16-bit payload into `buffer[0:1]` (received data at [0], received CRC at [1]) + +**Memory Access (`Memory_Access_1`)** +- Operation: read, offset: 0x00, size: 1 byte +- Reads `buffer[0]` — the received data byte — to pass to the CRC block for recomputation + +**Load Constant (`Load_Constant_0`)** +- Value: 0 — CRC init value + +**CRC (`CRC_0`)** +- Polynomial: 0x07 (same as PRU0) +- Recomputes the CRC over the received data byte +- Result is compared against the received CRC in `main.asm` to validate integrity + +--- + +### Shared Memory Layout and CRC Validation (main.asm) + +After both PRUs halt, `main.asm` writes results to SMEM (`0x30010000` on AM243x) and performs the comparison: + +``` +SMEM offset | Content +------------|-------------------------------------------- +0x00 | RX calculated CRC (PRU1 recomputed CRC) +0x01 | TX received CRC (CRC extracted from the received frame) +0x02 | Match flag: 0x01 = CRC match, 0xFF = CRC mismatch +0x03 | Raw received data byte (from RX frame) +0x04 | Original TX data byte (0xAA, written by PRU0) +``` + +The R5F reads these SMEM locations and prints the result over UART: + +``` +CRC MATCH → "Data=0xAA TX_CRC=0xXX RX_CRC=0xXX Status: PASS" +CRC MISMATCH→ "Data=0xAA TX_CRC=0xXX RX_CRC=0xXX Status: FAIL" +``` + +--- + # Supported Combinations Parameter | Value From 30e0a87722fb9799210845e0e60e7143e90562f1 Mon Sep 17 00:00:00 2001 From: Ayushman Date: Mon, 8 Jun 2026 16:32:01 +0530 Subject: [PATCH 4/5] am243x/am261x/am64x: improve documentation Signed-off-by: Ayushman --- docs/getting_started_mcuplus.md | 1 + examples/conditional/readme.md | 91 ++++++++++++++++ examples/gray_encoder_4_bit/readme.md | 96 +++++++++++++++++ .../am243x-lp/icss_g0_pru0_fw/example.syscfg | 8 +- examples/incremental_spi/readme.md | 101 +++++++++++++++++- examples/makefile | 1 + examples/spi_loopback/readme.md | 79 ++++++++++++++ 7 files changed, 372 insertions(+), 5 deletions(-) diff --git a/docs/getting_started_mcuplus.md b/docs/getting_started_mcuplus.md index 81508fc..a9fc131 100644 --- a/docs/getting_started_mcuplus.md +++ b/docs/getting_started_mcuplus.md @@ -22,6 +22,7 @@ Users can either install the prebuilt MCU+ SDK : installer can be downloaded here: - [AM243x MCU+ SDK](https://www.ti.com/tool/download/MCU-PLUS-SDK-AM243X) - [AM64x MCU+ SDK](https://www.ti.com/tool/download/MCU-PLUS-SDK-AM64X) + - [AM261x MCU+ SDK](https://www.ti.com/tool/download/MCU-PLUS-SDK-AM261X) ## Install the tools diff --git a/examples/conditional/readme.md b/examples/conditional/readme.md index eb51c36..5fe29b2 100644 --- a/examples/conditional/readme.md +++ b/examples/conditional/readme.md @@ -3,6 +3,97 @@ ## Introduction This example demonstrates the use of pru-no-code-tool for GPO set/clear operation using the if/else block, if the condition is true , we set GPO0 or else we clear GPO0 +--- + +## No-Code Tool Block Design + +This example demonstrates the If/Else block's branching behaviour and highlights a critical wiring rule: **each branch must be terminated with a Flow Control block**. + +### Why Flow Control Termination is Required + +The If/Else block generates a conditional branch instruction (`QBLT` in this case). The code generator lays out the FALSE path immediately after the branch, followed by the TRUE path at the branch target label. Without an explicit terminator at the end of the FALSE path, execution falls through into the TRUE path's code — meaning both GPO set and GPO clear would run regardless of the condition. + +Adding a Flow Control (HALT) block at the end of each branch prevents this fall-through. Each path executes its own GPO operation and halts independently. + +``` +Without termination (WRONG): With termination (CORRECT): + +QBLT TRUE_LABEL, R0, R1 QBLT TRUE_LABEL, R0, R1 +; FALSE path ; FALSE path + +; falls through into TRUE path! HALT ← Flow_Control_0 stops here +TRUE_LABEL: TRUE_LABEL: + + HALT ← Flow_Control_1 stops here +``` + +### Block Flow + +``` +[Load Constant: data_1] value = 4 ──┐ + ├──► [If/Else: If_Else_0] condition: input1 < input2 +[Load Constant: data_2] value = 5 ──┘ │ │ + │ T │ F + (4 < 5 (4 < 5 + = TRUE) = FALSE) + │ │ + ▼ ▼ + [GPO: set_gpo] [GPO: clear_gpo] + │ │ + ▼ ▼ + [Flow_Control_1] [Flow_Control_0] + HALT HALT +``` + +Since 4 < 5 is TRUE, the T branch executes: `set_gpo` runs and the PRU halts. The `clear_gpo` path is never reached. + +### Block Configuration Details + +**Load Constant (`data_1`)** +- Value: 4 — feeds If/Else input1 + +**Load Constant (`data_2`)** +- Value: 5 — feeds If/Else input2 (the threshold) + +**If/Else (`If_Else_0`)** +- Condition: `lessThanInput2` — TRUE when input1 < input2 (4 < 5 → TRUE) +- T port → `set_gpo` +- F port → `clear_gpo` +- 1 PRU cycle for the comparison and branch + +**PRU GPO (`set_gpo`)** +- Sets GPO0 HIGH on the TRUE branch + +**PRU GPO (`clear_gpo`)** +- Clears GPO0 LOW on the FALSE branch + +**Flow Control (`Flow_Control_1`)** +- HALT — terminates the TRUE branch after `set_gpo` +- Prevents fall-through into any code that follows + +**Flow Control (`Flow_Control_0`)** +- HALT — terminates the FALSE branch after `clear_gpo` +- Same reason — each branch needs its own explicit terminator + +### Generated Assembly Structure + +```asm + LDI Ra.b0, 4 ; data_1 + LDI Rb.b0, 5 ; data_2 + QBLT TRUE_LABEL, Ra.b0, Rb.b0 ; branch if Ra < Rb (4 < 5 → taken) + +; FALSE path (clear_gpo) + CLR R30, R30, ; clear GPO + HALT ; Flow_Control_0 + +TRUE_LABEL: +; TRUE path (set_gpo) + SET R30, R30, ; set GPO + HALT ; Flow_Control_1 +``` + +--- + # Supported Combinations Parameter | Value diff --git a/examples/gray_encoder_4_bit/readme.md b/examples/gray_encoder_4_bit/readme.md index 2c27edc..eaf7c0c 100644 --- a/examples/gray_encoder_4_bit/readme.md +++ b/examples/gray_encoder_4_bit/readme.md @@ -8,6 +8,102 @@ A 16-entry Lookup Table is pre-loaded into PRU DMEM by the R5F before the PRU st The R5F reads the result from PRU ICSSG0 Shared RAM (SMEM at `0x30010000`) and prints the binary input and its Gray code output over the UART console. +--- + +## No-Code Tool Block Design + +This example introduces two concepts working together: the **Lookup Table + Access Lookup Table** pair for O(1) data conversion, and the **Group block** for packaging that logic into a named callable subroutine. + +### The Group Block's Role + +All blocks in this design live inside a Group block named `gray_encoder`. A Group generates a standalone assembly subroutine — `gray_encoder_start` — rather than inline code. The PRU does not execute the group automatically; `main.asm` calls it explicitly with `CALL gray_encoder_start`, execution runs through the two blocks inside, and control returns to `main.asm` automatically (the tool appends `JMP RET_ADDR0`). + +This is the key difference from previous examples: the no-code blocks here define a **reusable function**, not a linear program. + +### Block Flow + +``` +main.asm: + CALL gray_encoder_start + │ + ▼ +┌─── [Group: gray_encoder] ──────────────────────────────┐ +│ │ +│ [Load Constant: binary_index] value = 5 │ +│ │ (index) │ +│ ▼ │ +│ [Access Lookup Table: gray_encode] │ +│ table: gray_encoder_lut │ +│ input: binary_index output (5) │ +│ output: Gray code at index 5 → 7 (0b0111) │ +│ │ +└─────────────────────────────────────────────────────────┘ + │ + ▼ (automatic return to main.asm) + result register holds Gray code value +``` + +### Block Configuration Details + +**Load Constant (`binary_index`)** +- Value: 5 — the 4-bit binary input to encode (range 0–15) +- Acts as the index into the lookup table +- Rename from the default `Load_Constant_0` to `binary_index` — makes the data flow self-documenting + +**Access Lookup Table (`gray_encode`)** +- Table: `gray_encoder_lut` +- input1: `binary_index` output (value 5) +- Performs a 5-cycle table lookup (LDI32 base address + LBBO byte read) +- Output: the Gray code byte at index 5 → **7** (0x07, binary `0111`) + +**Lookup Table (`gray_encoder_lut`)** +- Init pattern: manual +- 16 entries covering all 4-bit inputs (0–15): + +``` +Binary │ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +Gray │ 0 1 3 2 6 7 5 4 12 13 15 14 10 11 9 8 +``` + +This is the standard binary-to-Gray conversion (`Gray = N XOR (N >> 1)`). The table is pre-loaded into PRU DMEM by the R5F before the PRU starts. + +**Group (`gray_encoder`)** +- `$name`: `gray_encoder` (SysConfig instance ID) +- `groupName`: `gray_encoder` (generates assembly label `gray_encoder_start`) +- Contains: `binary_index`, `gray_encode` +- No Flow Control block needed — the tool automatically appends the return instruction +- Has no prev/next ports; called exclusively via `CALL` from `main.asm` + +### Generated Assembly Structure + +```asm +; sysconfig_generated_start section is empty (no ungrouped blocks) + +; Group subroutine — only executes when called +gray_encoder_start: + LDI Rx.b0, 5 ; binary_index = 5 + LDI32 Ry, ; load LUT base address + LBBO &Rx.b0, Ry, Rx.b0, 1 ; Rx.b0 = lut[5] = 7 + JMP RET_ADDR0 ; return to caller +``` + +### main.asm Integration + +```asm + .include "pru_syscfg.inc" ; required — defines CALL macro and RET_ADDR0 + .ref sysconfig_generated_start + .ref gray_encoder_start ; reference the group's generated label + +main: + zero &r0, 120 + CALL gray_encoder_start ; execute the encoder, result in output register + ; result (Gray code = 7) now available in register + ; main.asm stores it to SMEM for R5F to read + HALT +``` + +--- + # Supported Combinations Parameter | Value diff --git a/examples/incremental_spi/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg b/examples/incremental_spi/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg index f585af0..c0dab58 100644 --- a/examples/incremental_spi/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg +++ b/examples/incremental_spi/firmware/am243x-lp/icss_g0_pru0_fw/example.syscfg @@ -26,6 +26,7 @@ const pru_spi_write1 = pru_spi_write.addInstance(); * Write custom configuration values to the imported modules. */ pru_blocks_static_module.signalsToDisplay = ["3","4","5"]; + arithmetic_block1.$name = "Arithmetic_0"; load_constant_block1.$name = "Load_Constant_0"; @@ -35,9 +36,10 @@ load_constant_block2.constant1 = 1; flow_control_block1.$name = "Flow_Control_0"; -loop_block1.$name = "Loop_0"; -loop_block1.loopCount = 10; -loop_block1.$size = [500,315]; +loop_block1.$name = "Loop_0"; +loop_block1.loopCount = 10; +loop_block1.preInitBlocks = ["Load_Constant_0"]; +loop_block1.$size = [500,315]; pru_spi_write1.$name = "PRU0_SPI_Write_0"; pru_spi_write1["SDO Signal"] = "3"; diff --git a/examples/incremental_spi/readme.md b/examples/incremental_spi/readme.md index 1cd54dc..8073d02 100644 --- a/examples/incremental_spi/readme.md +++ b/examples/incremental_spi/readme.md @@ -6,16 +6,113 @@ This example demonstrates the use of pru-no-code-tool to send incremental data ( probe pin 19 (SDO) and pin 17 (SCLK) in case of am243x and pin 11 (SCLK) and pin 67 (SDO) in case of am261x and to observe the waveform which should be similar to the following snapshot taken from the logic analyzer
-incremental_spi +incremental_spi
Incremental data (1-10) sent through SPI , in the figure, white signal represent the Data and the brown signal represent the Clock
+--- + +## No-Code Tool Block Design + +This example's key feature is the Loop block's **Pre-Initialization** mechanism. Understanding why it matters here is the whole point of the example. + +### The Pre-Init Problem and Solution + +The loop body contains an ADD block that accumulates a counter: `result = result + 1`. For this to work across iterations, the register holding the initial value must be written **once** before the loop starts — not reset to 0 on every iteration. + +Without pre-init, `Load_Constant_0` executes inside the loop body every iteration, resetting the accumulator register back to 0 each time. The ADD then always computes `0 + 1 = 1`, and SPI sends `1, 1, 1, 1...` + +With `Load_Constant_0` marked as a **pre-init block**, the tool moves its `LDI` instruction to before the hardware `LOOP` instruction. The register is initialised to 0 once. Each iteration the ADD overwrites the same register with the incremented value, which persists into the next iteration. SPI sends `1, 2, 3, ... 10`. + +### Block Flow + +``` +PRE-INIT (executes once before loop starts): + [Load Constant: Load_Constant_0] value = 0 → initialises accumulator register + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +[Loop: Loop_0] 10 iterations +┌──────────────────────────────────────────────────┐ +│ [Load Constant: Load_Constant_1] value = 1 │ +│ │ │ +│ │ (increment) │ +│ ▼ │ +│ [Arithmetic: Arithmetic_0] ADD │ +│ input1 ← Load_Constant_0 register │ +│ input2 ← Load_Constant_1 (1) │ +│ output → overwrites accumulator register │ +│ │ │ +│ ▼ │ +│ [SPI Write: PRU0_SPI_Write_0] │ +│ transmit accumulator value over SPI │ +└──────────────────────────────────────────────────┘ + │ + ▼ +[Flow Control: Flow_Control_0] HALT +``` + +**Result per iteration:** register = 0+1=1, 1+1=2, 2+1=3 ... 9+1=10 → SPI sends `1, 2, 3, 4, 5, 6, 7, 8, 9, 10`. + +### Block Configuration Details + +**Load Constant (`Load_Constant_0`)** +- Value: 0 (default) — accumulator seed +- Marked as **pre-init block** on `Loop_0`: its `LDI R, 0` is emitted once before the `LOOP` instruction, not inside the loop body + +**Load Constant (`Load_Constant_1`)** +- Value: 1 — the fixed increment added each iteration +- Executes inside the loop body every iteration (not pre-init) + +**Arithmetic (`Arithmetic_0`)** +- Operation: ADD (default) +- input1: `Load_Constant_0` output register (the accumulator — initialised to 0 pre-loop, then holds the running total) +- input2: `Load_Constant_1` output (constant 1) +- output: written back to the same register as input1, so the value carries forward to the next iteration + +**Loop (`Loop_0`)** +- Loop count: 10 +- Pre-init blocks: `Load_Constant_0` — ensures the accumulator register is zeroed exactly once +- Contains: `Load_Constant_0`, `Load_Constant_1`, `Arithmetic_0`, `PRU0_SPI_Write_0` + +**SPI Write (`PRU0_SPI_Write_0`)** +- Packet size: 8 bits (default) +- SDO signal: GPO3, SCLK signal: GPO4, CS signal: GPO5 +- Transmits the accumulator value after each ADD — one SPI frame per loop iteration + +**Flow Control (`Flow_Control_0`)** +- Operation: HALT — stops PRU0 after all 10 frames are sent + +### Generated Assembly Structure + +```asm +; Pre-init: Load_Constant_0 emitted here (outside loop) +LDI Rx.b0, 0 ; initialise accumulator once + +; Loop overhead +LDI Ry.b0, 10 ; loop counter +LOOP Loop_0_end, Ry.b0 + + ; Load_Constant_1 (inside loop body) + LDI Rz.b0, 1 + + ; Arithmetic_0: ADD + ADD Rx.b0, Rx.b0, Rz.b0 ; Rx = Rx + 1 (1, 2, 3 ... 10) + + ; PRU0_SPI_Write_0: transmit Rx.b0 over SPI + ; ... SPI bit-bang instructions ... + +Loop_0_end: +HALT +``` + +--- + # Supported Combinations Parameter | Value ---------------|----------- ICSSG | ICSSG0 - PRU0 - ICSSM | ICSSM1 - PRU0, PRU1 (am261x only) + ICSSM | ICSSM1 - PRU0 (am261x only) Toolchain | pru-cgt Board | am243x-lp, am261x-lp Example folder | examples/incremental_spi/ diff --git a/examples/makefile b/examples/makefile index 9282f09..fd15cde 100644 --- a/examples/makefile +++ b/examples/makefile @@ -7,6 +7,7 @@ SUBDIRS := \ conditional \ gray_encoder_4_bit \ incremental_spi \ + tamagawa_single_channel \ # "make" or "make all" builds projects that match $(DEVICE) set in imports.mak all: ARGUMENTS = all diff --git a/examples/spi_loopback/readme.md b/examples/spi_loopback/readme.md index 5b12e3e..06ebb6f 100644 --- a/examples/spi_loopback/readme.md +++ b/examples/spi_loopback/readme.md @@ -3,6 +3,85 @@ ## Introduction This example demonstrates the use of pru-no-code-tool for SPI loopback, the code sends 32 bits of data from PRU0 to PRU1 through SPI protocol, PRU0 acts as controller (master) and PRU1 acts as preipheral (slave) in this example +--- + +## No-Code Tool Block Design + +This is one of the simplest no-code designs: PRU0 needs just three blocks to drive a full 32-bit SPI transaction, and PRU1 needs only two. No memory buffers, no CRC, no branching. + +### PRU0 — SPI Controller Block Flow + +``` +[Load Constant: Load_Constant_0] value = 0xABCD1234 (32-bit data to transmit) + │ + ▼ +[SPI Write: PRU_SPI_Write_0] transmit 32 bits, controller mode + │ SCLK high = 12 PRU cycles, low = 10 PRU cycles + │ SDO → GPO3, SCLK → GPO4, CS → GPO5 + ▼ +[Flow Control: Flow_Control_0] HALT +``` + +### PRU1 — SPI Peripheral Block Flow + +``` +[SPI Read: PRU_SPI_Read_0] receive 32 bits, peripheral mode + │ waits for CS assert from PRU0, then clocks in data + │ CS ← GPI6, SCLK ← GPI11 + ▼ +[Flow Control: Flow_Control_0] HALT (received word available in output register) +``` + +### Block Configuration Details + +**PRU0 Blocks** + +**Load Constant (`Load_Constant_0`)** +- Value: 2882382797 (0xABCD1234) — the 32-bit test pattern to send over SPI + +**SPI Write (`PRU_SPI_Write_0`)** +- Packet size: 32 bits +- Device mode: controller (drives SCLK and CS) +- SCLK high pulse width: 12 PRU cycles (60 ns at 200 MHz) +- SCLK low pulse width: 10 PRU cycles (50 ns at 200 MHz) +- SDO signal: GPO3 (`PRG0_PRU0_GPO3`) — data out to peripheral +- SCLK signal: GPO4 (`PRG0_PRU0_GPO4`) — clock to peripheral +- CS signal: GPO5 (`PRG0_PRU0_GPO5`) — chip select to peripheral + +**Flow Control (`Flow_Control_0`)** +- Operation: HALT — stops PRU0 after the single SPI transaction + +--- + +**PRU1 Blocks** + +**SPI Read (`PRU_SPI_Read_0`)** +- Packet size: 32 bits +- Device mode: peripheral (waits for CS and SCLK from controller) +- SCLK high pulse width: 12 PRU cycles, low: 10 PRU cycles (must match PRU0) +- CS signal: GPI6 (`PRG0_PRU1_GPO6`) — chip select from controller +- SCLK signal: GPI11 (`PRG0_PRU1_GPO11`) — clock from controller +- Received word is left in the output register for the R5F to read from SMEM + +**Flow Control (`Flow_Control_0`)** +- Operation: HALT — stops PRU1 after the single SPI receive + +--- + +### Pin Connections Summary + +PRU0 (controller) drives three signals; PRU1 (peripheral) receives them. The wires that must be connected on the board are: + +| Signal | PRU0 pin (controller) | PRU1 pin (peripheral) | +|--------|-----------------------|----------------------| +| SDO→SDI | GPO3 / J2.2 | GPO1 / J7.7 | +| CS | GPO5 / J2.8 | GPO6 / J7.9 | +| SCLK | GPO4 / J2.4 | GPO11 / J7.10 | + +The PRU1 SDO→PRU0 SDI path exists in hardware but is unused here since this is a write-only loopback test. + +--- + # Supported Combinations Parameter | Value From 3ca08ca752bfe4fd4a2250be4e5a1d8549e53a86 Mon Sep 17 00:00:00 2001 From: Ayushman Date: Mon, 8 Jun 2026 17:00:15 +0530 Subject: [PATCH 5/5] am243x/am64x/am261x: add broken link checker Signed-off-by: Ayushman --- .github/workflows/link_check.yml | 53 +++++++++ README.md | 7 +- .../uart_config_block.md | 96 +++++++++++++++++ .../uart_rx_block.md | 101 ++++++++---------- .../uart_tx_block.md | 100 ++++++++--------- docs/utils/data_splitter.md | 59 ++++++++++ .../memory-configuration/memory_access.md | 4 +- .../images/{UART_CRC_ss.png => uart_crc.png} | Bin examples/uart_crc/readme.md | 2 +- 9 files changed, 307 insertions(+), 115 deletions(-) create mode 100644 .github/workflows/link_check.yml create mode 100644 docs/input-output/serial-protocol-emulation/uart_config_block.md create mode 100644 docs/utils/data_splitter.md rename examples/uart_crc/images/{UART_CRC_ss.png => uart_crc.png} (100%) diff --git a/.github/workflows/link_check.yml b/.github/workflows/link_check.yml new file mode 100644 index 0000000..4a66578 --- /dev/null +++ b/.github/workflows/link_check.yml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (C) 2026 Texas Instruments Incorporated - http://www.ti.com/ +# +# Link Check CI — checks for broken relative links and image references in all +# markdown files across the repository. Catches missing images, wrong paths, and +# stale cross-references before they land in main. + +name: Link Check + +on: + push: + branches: [ main, master ] + paths: + - '**/*.md' + - '**/images/**' + pull_request: + branches: [ "**" ] + paths: + - '**/*.md' + - '**/images/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + link-check: + name: Check Links in Markdown Files + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run lychee link checker + uses: lycheeverse/lychee-action@v2 + with: + # Check all markdown files in the repo + args: >- + --verbose + --no-progress + --include-fragments + --accept 200..=429 + --root-dir ${{ github.workspace }} + './**/*.md' + # Fail the workflow if any broken links are found + fail: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index b4b6906..5511f7f 100644 --- a/README.md +++ b/README.md @@ -162,10 +162,9 @@ Blocks for GPIO, SPI, UART, and memory operations. | SPI Read | Read data via SPI | Variable | Up to 28.57 MHz | | SPI Write | Write data via SPI | Variable | Up to 28.57 MHz | | SPI Transfer | Full-duplex SPI | Variable | Up to 25.00 MHz | -| UART TX | Transmit UART frame (up to 62 data bits) | Variable | Up to 192 Mbaud | -| UART RX | Receive UART frame | Variable | Up to 192 Mbaud | - -**Note**: SPI maximum frequencies vary by mode. See [PRU I/O Blocks README](pru_io_blocks/README.md) for detailed frequency tables by SPI mode and block type. +| UART config | configure 3 channel interface for UART transmission | Variable | N/A | +| UART TX op | Transmit UART frame (up to 62 data bits) | Variable | Up to 32 Mbaud | +| UART RX op | Receive UART frame (up to 64 bits) | Variable | Up to 32 Mbaud | ### Utility Blocks diff --git a/docs/input-output/serial-protocol-emulation/uart_config_block.md b/docs/input-output/serial-protocol-emulation/uart_config_block.md new file mode 100644 index 0000000..5c53968 --- /dev/null +++ b/docs/input-output/serial-protocol-emulation/uart_config_block.md @@ -0,0 +1,96 @@ +--- + +## UART Config Block + +### Purpose + +Configures the PRU-ICSS ENDAT peripheral for UART TX, RX, or both. Run this block **once at init** before any `UART TX Op` or `UART RX Op` blocks. A single Global Reinit covers both TX and RX setup. Only one instance of this block is allowed per design. + +### Features + +- Combined TX + RX peripheral configuration in a single block +- TX and RX can use **different channels** (e.g. TX on CH0, RX on CH2) +- Independently enable TX Config, RX Config, or both +- Configurable baud rate, clock source, bit order, start/stop polarity +- RX: configurable oversampling (1x, 2x, 4x, 8x) for noise immunity +- TX: auto-detects single-shot vs continuous mode from the paired `UART TX Op` block +- Only one instance allowed per design (`maxInstances: 1`) + +### Configuration + +#### Common + +| Parameter | Description | Options | +|-----------|-------------|---------| +| PRU Selection | PRU core (auto-detected, read-only) | PRU0, PRU1 | +| Enable TX Config | Show and apply TX configuration | true / false | +| Enable RX Config | Show and apply RX configuration | true / false | + +At least one of TX or RX must be enabled. + +#### TX Parameters (visible when Enable TX Config = true) + +| Parameter | Description | Options / Range | +|-----------|-------------|-----------------| +| TX Channel | ENDAT channel for TX | CH0, CH1, CH2 | +| TX Clock Source | TX oversample clock | 192 MHz (UART_CLK), 200 MHz (CORE_CLK) | +| TX Baud Rate (MHz) | Desired TX baud rate | Must divide evenly into clock source | +| TX Clock Divider | Calculated divider (read-only) | (clockSource / baudRate) - 1 | +| TX Start Bit Polarity | Logic level of TX start bit | 0 (Low/Space), 1 (High/Mark) | +| TX Stop Bit Polarity | Logic level of TX stop bit | 0 (Low/Space), 1 (High/Mark) | +| TX Bit Swap (LSB First) | Bit transmission order | true (LSB first), false (MSB first) | +| TX Mode | Derived from UART TX Op's Data Bits (read-only) | Single-shot (≤29 bits), Continuous (≥30 bits) | + +#### RX Parameters (visible when Enable RX Config = true) + +| Parameter | Description | Options / Range | +|-----------|-------------|-----------------| +| RX Channel | ENDAT channel for RX | CH0, CH1, CH2 | +| RX Clock Source | RX oversample clock | 192 MHz (UART_CLK), 200 MHz (CORE_CLK) | +| RX Baud Rate (MHz) | Desired RX baud rate | Must divide evenly into (clock / oversample) | +| RX Clock Divider | Calculated divider (read-only) | (clockSource / (baudRate × oversample)) - 1 | +| RX Oversample Size | Samples per bit | 1x, 2x, 4x, 8x | +| RX Start Bit Polarity | Edge that triggers frame start | Falling Edge (0), Rising Edge (1) | +| RX Bit Swap (LSB First) | Bit reception order | true (LSB first), false (MSB first) | + +### Clock Divider Calculations + +**TX:** +``` +txClockDivider = (clockSource / baudRate) - 1 +``` +Example: 192 MHz clock, 12 MHz baud → divider = (192 / 12) - 1 = 15 + +**RX:** +``` +rxClockDivider = (clockSource / (baudRate × oversample)) - 1 +``` +Example: 192 MHz clock, 12 MHz baud, 8x oversample → divider = (192 / 96) - 1 = 1 + +### Generated Sequence + +1. Global Reinit (R31 bit 19) FIRST — flushes FIFO and state machines (TRM-mandated: reinit before de-asserting rx_en) +2. Poll busy bit (R31 bit 5) — deterministic wait for reinit completion +3. De-assert rx_en (R30.b3 = 0x00) AFTER reinit — handles stuck rx_en from a previous run +4. GPCFG write — enables peripheral interface mode (once, shared by TX and RX) +5. *(if TX enabled)* TXCFG write, TX CH_CFG0 write (frame size + bit-swap), R30.w2 channel select + clk_mode=1 +6. *(if RX enabled)* RXCFG write (clock divider, clock source, oversample, start bit polarity), RX CH_CFG0 write (frame size + bit-swap) + +### Ports + +This block has no data input or output ports — it only has `prev` and `next` control flow ports. + +### Performance + +~20 cycles for full peripheral configuration (TX + RX). + +### Important Notes + +- Only **one instance** of this block is allowed per design +- **Must appear before** any `UART TX Op` or `UART RX Op` blocks in the control flow +- RX frame size is read from the first `UART RX Op` instance's `RX Frame Size` setting +- TX data bits (used for TX_FRAME_SIZE / mode selection) are read from the first `UART TX Op` instance's `Data Bits` setting +- TX and RX can operate on different channels simultaneously (e.g. TX on CH0, RX on CH2) +- GPCFG is written once regardless of TX-only, RX-only, or both configurations + +--- diff --git a/docs/input-output/serial-protocol-emulation/uart_rx_block.md b/docs/input-output/serial-protocol-emulation/uart_rx_block.md index c7650ca..63bbdcf 100644 --- a/docs/input-output/serial-protocol-emulation/uart_rx_block.md +++ b/docs/input-output/serial-protocol-emulation/uart_rx_block.md @@ -1,54 +1,32 @@ --- -## UART RX Block (Hardware-Accelerated via ENDAT) +## UART RX Op Block ### Purpose -Configures the PRU-ICSS ENDAT peripheral hardware for UART reception and receives one complete UART frame. The block handles peripheral initialization, start-bit detection, oversampled bit collection, frame extraction, and outputs the received data. +Receives one UART frame using the ENDAT peripheral. Handles only the per-reception work: assert rx_en, poll and accumulate all frame bits, extract data, de-assert rx_en. **The `UART Config` block (with RX Config enabled) must appear earlier in the control flow** to set up the peripheral registers before this block is called. ### Features - Hardware-accelerated reception using the ICSSG ENDAT peripheral -- Up to 12 Mbaud at 192 MHz clock with 8x oversample - Three independent channels (CH0, CH1, CH2) -- Configurable oversampling (1x, 2x, 4x, 8x) -- LSB-first or MSB-first bit order -- Configurable start bit polarity -- Frame sizes 3–32 bits (1–30 data bits) +- Supports frame sizes 3–64 bits (1–62 data bits) +- LSB-first or MSB-first bit order (from UART Config block) +- Configurable oversampling (1x, 2x, 4x, 8x) (from UART Config block) +- No peripheral register writes — fast, suitable for repeated calls in a loop +- 32-bit output for ≤30 data bits; 64-bit output for 31–62 data bits (extended mode) -### Configuration - -| Parameter | Description | Options / Range | -|-----------|-------------|-----------------| -| PRU Selection | PRU core (auto-detected, read-only) | PRU0, PRU1 | -| Channel Selection | ENDAT channel | CH0, CH1, CH2 | -| Clock Source | RX oversample clock source | 192 MHz (UART_CLK), 200 MHz (CORE_CLK) | -| Baud Rate (MHz) | Desired baud rate | Must divide evenly into (clock / oversample) | -| Oversample Size | Samples per bit | 1x, 2x, 4x, 8x | -| Start Bit Polarity | Edge that triggers frame start | Falling Edge (0), Rising Edge (1) | -| Frame Size (bits) | Total bits including start + stop | 3–32 | -| Bit Swap (LSB First) | Bit order | true (LSB first), false (MSB first) | +### Prerequisites -### Clock Divider Calculation +A `UART Config` block with **RX Config enabled** must be placed earlier in the control flow. All RX parameters (channel, baud rate, oversample size, bit order, start bit polarity) are read automatically from that block. -``` -clockDivider = (clockSource / (baudRate × oversample)) - 1 -``` - -Example: 192 MHz clock, 12 MHz baud, 8x oversample → divider = (192 / 96) - 1 = 1 +### Configuration -### Valid Baud Rates (for 192 MHz, 8x oversample) +| Parameter | Description | Range | +|-----------|-------------|-------| +| RX Frame Size (bits) | Total bits per frame = data bits + 2 (start + stop) | 3–64 | -| Baud Rate | Clock Divider | -|-----------|---------------| -| 24 MHz | 0 | -| 12 MHz | 1 | -| 8 MHz | 2 | -| 6 MHz | 3 | -| 4 MHz | 5 | -| 3 MHz | 7 | -| 2 MHz | 11 | -| 1 MHz | 23 | +All other parameters are inherited from the paired `UART Config` block. ### Frame Size @@ -56,29 +34,35 @@ Example: 192 MHz clock, 12 MHz baud, 8x oversample → divider = (192 / 96) - 1 frameSize = dataBits + 2 (1 start bit + N data bits + 1 stop bit) ``` -Standard UART 8-bit: frameSize = 10 +| Protocol | Data Bits | RX Frame Size | +|----------|-----------|---------------| +| Standard UART 8-bit | 8 | 10 | +| UART 16-bit payload | 16 | 18 | +| UART 24-bit payload | 24 | 26 | +| UART 30-bit payload | 30 | 32 | +| UART 31-bit payload (extended) | 31 | 33 | -### How It Works +### Generated Sequence -1. **Global Reinit** (R31 bit 19) — assert reinit first to clear TX/RX state machines -2. **Delay** — 20 NOP cycles to let reinit settle -3. **De-assert rx_en** — clear R30[26:24] after reinit has settled (TRM sequence) -4. **Configure GPCFG** — set peripheral interface mode for the selected PRU -5. **Configure RXCFG** — set clock divider, clock source, oversample size, start bit polarity -6. **Configure CHx_CFG0** — set frame size and bit order -7. **Assert rx_en** — enable the selected channel (R30 bit 24/25/26) -8. **Bit accumulation loop** — poll valid flag (R31[26:24]), read oversampled byte, extract middle sample, accumulate bits -9. **Data extraction** — remove start and stop bits, align data to bit 0 -10. **Disable rx_en** — clear R30[26:24] after reception (TRM step 7 — resets all counters/flags) +1. Assert rx_en for the configured channel (R30[24/25/26] for CH0/CH1/CH2) +2. Loop frameSize times: poll valid flag (R31[24/25/26]), read middle oversample bit from R31 byte, clear flag, accumulate into output register +3. Extract data — shift to align, remove start and stop bits, mask to data width +4. De-assert rx_en (R30.b3 = 0x00) ### Output Port -- **output1**: Received data (4 bytes for ≤30 data bits) +| RX Frame Size | Data Bits | Output Port | Register(s) | +|---------------|-----------|-------------|-------------| +| 3–32 bits | 1–30 bits | 32-bit (output32) | 1 register | +| 33–64 bits | 31–62 bits | 64-bit (output64) | 2 consecutive registers | + +- **output1** (32-bit mode): received data word in a single register +- **output1** (64-bit mode): lower 32 bits in the allocated register, upper bits in the next consecutive register -### Oversample and Middle Bit Selection +### Oversample Middle Bit Selection | Oversample | Middle Sample Bit | -|-----------|-----------------| +|-----------|-------------------| | 1x | Bit 0 | | 2x | Bit 0 | | 4x | Bit 2 | @@ -86,16 +70,15 @@ Standard UART 8-bit: frameSize = 10 ### Performance -- ~25–30 cycles for peripheral configuration -- Variable reception time depending on baud rate and frame size -- Each bit: poll valid flag + read + clear + accumulate (~5–10 cycles per bit) -- Typical 10-bit frame: ~50–100 cycles total +- ~10 + (frameSize × 5) cycles per reception +- No peripheral register writes overhead — only rx_en assert/de-assert and the bit polling loop ### Important Notes -- The ENDAT peripheral is half-duplex — TX and RX cannot operate concurrently on the same channel -- Global reinit clears all pending RX data — any frame in progress is lost -- The valid flag (R31[26/25/24]) must be cleared after each bit read to prevent overflow -- rx_en is cleared at end of reception to reset counters cleanly for the next frame +- **UART Config must appear before UART RX Op** in the control flow +- **All UART RX Op instances must use the same RX Frame Size** — uart_config uses the first instance's value; mismatched instances generate incorrect assembly +- The valid flag (R31[24/25/26]) must be cleared after each bit read to prevent overflow +- rx_en is de-asserted at end of reception to reset counters cleanly for the next frame +- frameSize > 32 activates extended mode — output port becomes 64-bit; downstream blocks must accept 64-bit input --- diff --git a/docs/input-output/serial-protocol-emulation/uart_tx_block.md b/docs/input-output/serial-protocol-emulation/uart_tx_block.md index 215fb95..86c0184 100644 --- a/docs/input-output/serial-protocol-emulation/uart_tx_block.md +++ b/docs/input-output/serial-protocol-emulation/uart_tx_block.md @@ -1,85 +1,87 @@ --- -## UART TX Block (Hardware-Accelerated via ENDAT) +## UART TX Op Block ### Purpose -Configures the PRU-ICSS ENDAT peripheral hardware for UART transmission and sends one complete UART frame. The block handles peripheral initialization, frame construction, FIFO loading, and waits for transmission to complete. +Transmits one UART frame using the ENDAT peripheral. Handles only the per-transmission work: per-command reinit, frame construction, FIFO loading, start trigger, and wait for TX complete. **The `UART Config` block (with TX Config enabled) must appear earlier in the control flow** to set up the peripheral registers before this block is called. ### Features - Hardware-accelerated transmission using the ICSSG ENDAT peripheral -- Up to 32 MHz baud at 192 MHz clock - Three independent channels (CH0, CH1, CH2) - 1–62 data bits per frame - Single-shot mode (1–29 bits) and continuous FIFO mode (30–62 bits) -- LSB-first or MSB-first bit order -- Independent start and stop bit polarity control +- LSB-first or MSB-first bit order (from UART Config block) +- Independent start and stop bit polarity control (from UART Config block) +- No peripheral register writes — only per-command reinit, frame construction, FIFO load, TX trigger -### Configuration +### Prerequisites -| Parameter | Description | Options / Range | -|-----------|-------------|-----------------| -| PRU Selection | PRU core (auto-detected, read-only) | PRU0, PRU1 | -| Channel Selection | ENDAT channel | CH0, CH1, CH2 | -| Clock Source | TX clock source | 192 MHz (UART_CLK), 200 MHz (CORE_CLK) | -| Baud Rate (MHz) | Desired baud rate | Must divide evenly into clock source | -| Start Bit Polarity | Logic level of start bit | 0 (Low), 1 (High) | -| Stop Bit Polarity | Logic level of stop bit | 0 (Low), 1 (High) | -| Bit Swap (LSB First) | Bit order | true (LSB first), false (MSB first) | -| Data Bits | Number of data bits (NOT including start/stop) | 1–62 | +A `UART Config` block with **TX Config enabled** must be placed earlier in the control flow. All TX parameters (channel, baud rate, bit order, start/stop polarity) are read automatically from that block. -### Clock Divider Calculation +### Configuration -``` -clockDivider = (clockSource / baudRate) - 1 -``` +| Parameter | Description | Range | +|-----------|-------------|-------| +| Data Bits | Number of data payload bits (excluding start and stop bits) | 1–62 | -Example: 192 MHz clock, 12 MHz baud → divider = (192 / 12) - 1 = 15 +All other parameters are inherited from the paired `UART Config` block. ### Transmission Modes | Data Bits | Frame Size | Mode | FIFO Strategy | |-----------|------------|------|---------------| | 1–29 | 3–31 bits | Single-shot | Pre-load 1–4 bytes, TX GO, wait for busy to clear | -| 30–62 | 32–64 bits | Continuous | Pre-load 4 bytes, TX GO, poll FIFO ≤2 to load remaining | +| 30–32 | 32–34 bits | Specific-bit | 4 bytes pre-load + byte 5 via continuous polling (if needed) | +| 33–62 | 35–64 bits | Continuous | 4 bytes pre-load, TX GO, poll FIFO ≤2 to load remaining bytes | + +Frame size = dataBits + 2. Total FIFO bytes = ceil(frameSize / 8). + +### Generated Sequence + +1. Per-command reinit (R31 bit 19) — clears FIFO and state machines (TRM-mandated order: reinit before de-asserting rx_en) +2. Poll reinit busy bit (R31[5/13/21] for CH0/CH1/CH2) +3. De-assert rx_en (R30.b3 = 0x00) +4. Channel select + clk_mode write to R30.w2 +5. Frame construction — insert start bit, shift data bits, insert stop bit (LSB or MSB order) +6. FIFO load — 1–4 bytes for single-shot (dataBits ≤ 29); 4 bytes + continuous polling for dataBits 30–32; 4 bytes + byte 5+ polling for dataBits 33–62 +7. Start transmission (R31 bit 18 = tx_channel_go) +8. Wait for TX complete (poll R31[5/13/21] busy bit) -Frame size = dataBits + 2 (1 start + data bits + 1 stop). Total FIFO bytes = ceil(frameSize / 8). +### Input Port -### How It Works +| Data Bits | Input Port | Register(s) | +|-----------|------------|-------------| +| 1–32 bits | 32-bit (input32) | 1 register | +| 33–62 bits | 64-bit (input64) | 2 consecutive registers | -1. **Global Reinit** (R31 bit 19) — clear FIFO and reset TX/RX state machines -2. **Delay** — 20 NOP cycles -3. **Configure GPCFG** — set peripheral interface mode for the selected PRU -4. **Configure TXCFG** — set clock divider and clock source -5. **Configure CHx_CFG0** — set frame size (single-shot) or 0 (continuous), and bit-swap flag -6. **Select channel** — write channel number to R30[17:16] -7. **Construct frame** — insert start and stop bits around the data, align for MSB/LSB order -8. **Load FIFO** — write bytes to R30[7:0] (each write pushes one byte into the TX FIFO) -9. **Start TX** — set R31 bit 18 (tx_channel_go) -10. **Continuous mode** — poll tx_fifo_sts (R31[4:2] for CH0) and load remaining bytes when FIFO ≤ 2 -11. **Wait for completion** — poll tx_global_reinit_active/busy (R31 bit 5/13/21 for CH0/1/2) +- **input1** (32-bit mode): data word to transmit in a single register +- **input1** (64-bit mode): lower 32 bits in the allocated register, upper bits in the next consecutive register -### Input Ports +### Common Data Bit Configurations -| Port | Used When | Description | -|------|-----------|-------------| -| input1 | Always | Lower 32 bits of data to transmit | -| input2 | dataBits > 32 only | Upper bits of data (continuous mode) | +| Protocol | Data Bits | Frame Bits | Mode | +|----------|-----------|------------|------| +| Standard UART 8-bit | 8 | 10 | Single-shot | +| 16-bit payload | 16 | 18 | Single-shot | +| 24-bit payload | 24 | 26 | Single-shot | +| 29-bit payload (max single-shot) | 29 | 31 | Single-shot | +| 30-bit payload | 30 | 32 | Specific-bit | +| 32-bit payload | 32 | 34 | Specific-bit | +| 33-bit payload | 33 | 35 | Continuous | ### Performance -| Mode | Approximate Cycles | -|------|-------------------| -| Single-shot (1–29 bits) | ~25 + numFifoBytes | -| Continuous (30–62 bits) | ~30 + (continuousBytes × 6) | +- Single-shot (1–29 bits): ~6 + numFifoBytes cycles +- Continuous (30–62 bits): ~15 + (continuousBytes × 6) cycles ### Important Notes -- **dataBits** is ONLY the data payload — the block automatically adds start and stop bits -- Frame size = dataBits + 2; total FIFO bytes = ceil(frameSize / 8) -- The ENDAT peripheral is half-duplex — TX and RX cannot operate concurrently on the same channel -- For dataBits > 32, connect two inputs: `input1` for lower 32 bits, `input2` for upper bits -- Standard UART convention: start bit = 0 (Low), stop bit = 1 (High), LSB first +- **UART Config must appear before UART TX Op** in the control flow +- **All UART TX Op instances must use the same Data Bits value** — uart_config uses the first instance's value for TX_FRAME_SIZE; mismatched instances generate incorrect assembly +- **dataBits** is only the data payload — start and stop bits are added automatically +- dataBits > 32 activates continuous mode — input port becomes 64-bit; the upstream data source block must produce a 64-bit output +- Standard UART convention: start bit = 1 (High/Mark), stop bit = 0 (Low/Space), LSB first --- diff --git a/docs/utils/data_splitter.md b/docs/utils/data_splitter.md new file mode 100644 index 0000000..6463576 --- /dev/null +++ b/docs/utils/data_splitter.md @@ -0,0 +1,59 @@ +--- + +## Data Splitter Block + +### Purpose + +Extracts either the lower or upper 32 bits from a 64-bit input (two consecutive registers) and outputs it as a 32-bit value. Use this to consume one half of a 64-bit output produced by blocks such as `UART RX Op` in extended mode or a `Memory Access` block with data size > 4 bytes. + +### Features + +- Single-instruction extraction (one MOV) — 1 cycle +- Selectable lower half (bits 31:0) or upper half (bits 63:32) +- 64-bit input, 32-bit output — bridges 64-bit producers to 32-bit consumers + +### Configuration + +| Parameter | Description | Options | +|-----------|-------------|---------| +| Split Select | Which 32-bit half to extract from the 64-bit input | Lower 32 bits (bits 31:0), Upper 32 bits (bits 63:32) | + +### Ports + +| Port | Type | Description | +|------|------|-------------| +| input1 | 64-bit (input64) | 64-bit data input from a two-register source | +| output1 | 32-bit (output32) | Selected 32-bit half | + +### Generated Assembly + +**Lower 32 bits (Split Select = lower):** +```assembly +MOV dataOut, dataRegLo ; copy lower register (bits 31:0) +``` + +**Upper 32 bits (Split Select = upper):** +```assembly +MOV dataOut, dataRegHi ; copy upper register (bits 63:32) +``` + +### Performance + +1 cycle regardless of selection. + +### Typical Use Case + +`UART RX Op` with `RX Frame Size > 32` produces a 64-bit output. Connect it to two Data Splitter blocks — one set to `lower`, one to `upper` — to route each 32-bit half independently downstream. + +``` +[UART RX Op (64-bit out)] ──► [Data Splitter lower] ──► [downstream block A (32-bit in)] + └──► [Data Splitter upper] ──► [downstream block B (32-bit in)] +``` + +### Important Notes + +- The input port is fixed at 64-bit — it must be connected to a 64-bit output port +- The output port is fixed at 32-bit — downstream blocks must accept a 32-bit input +- No arithmetic is performed — this is purely a register copy + +--- diff --git a/docs/utils/memory-configuration/memory_access.md b/docs/utils/memory-configuration/memory_access.md index 4d381df..40a7257 100644 --- a/docs/utils/memory-configuration/memory_access.md +++ b/docs/utils/memory-configuration/memory_access.md @@ -11,7 +11,7 @@ Reads from or writes to a named memory buffer defined by a Memory Variable block - Read (LBBO) and Write (SBBO) modes - Accesses named symbols defined by Memory Variable blocks - Byte offset from symbol base address -- Data sizes 1–112 bytes +- Data sizes 1-8 bytes - Compile-time bounds checking (offset + size ≤ buffer size) - 3-cycle execution @@ -23,7 +23,7 @@ Reads from or writes to a named memory buffer defined by a Memory Variable block | Memory Variable Selected | Shows which Memory Variable is currently bound to this block | — | | Memory Variable (nested) | Configure the auto-created buffer's label, size, and location directly below this block | — | | Offset (bytes) | Byte offset from symbol base | 0 to (buffer size - data size) | -| Data Size (bytes) | Number of bytes to access | 1–112 | +| Data Size (bytes) | Number of bytes to access | 1–8 | When the Memory Access block is added to the design, a Memory Variable block is automatically created and nested under it. Configure the buffer through the nested block. If multiple diff --git a/examples/uart_crc/images/UART_CRC_ss.png b/examples/uart_crc/images/uart_crc.png similarity index 100% rename from examples/uart_crc/images/UART_CRC_ss.png rename to examples/uart_crc/images/uart_crc.png diff --git a/examples/uart_crc/readme.md b/examples/uart_crc/readme.md index 0546bbf..7512788 100644 --- a/examples/uart_crc/readme.md +++ b/examples/uart_crc/readme.md @@ -7,7 +7,7 @@ This example demonstrates the use of pru-no-code-tool for sending a data byte an The data and its CRC are transmitted in a single UART frame whose snapshot from the logic analyzer is attached below :
-uart_crc +uart_crc
The data and its CRC is transmitted in a single frame (start bit 1 and stop bit 0) , white signal -> data+CRC, brown -> tx enable, red -> clock