X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=fx2lafw.c;h=73fbf2032239f0a84a1c900d0cbfe24b2f55f525;hb=fc6f8a337f2d4ab3a1c31a1a5a45e03aa0b53f6f;hp=7972060f81de411c679c7d32b4bd98f75eb7086f;hpb=4ad20a4c57699f7ea5ce88fc132feaf7471743c1;p=sigrok-firmware-fx2lafw.git diff --git a/fx2lafw.c b/fx2lafw.c index 7972060f..73fbf203 100644 --- a/fx2lafw.c +++ b/fx2lafw.c @@ -28,10 +28,9 @@ * * - We use the FX2 in GPIF mode to sample the data (asynchronously). * - We use the internal 48MHz clock for GPIF. - * - The 8 channels/pins we sample (the GPIF data bus) are PB0-PB7. - * Support for 16 channels is not yet included, but might be added later. - * - Endpoint 2 is used for data transfers from FX2 to host. - * - The endpoint is quad-buffered. + * - The 8 channels/pins we sample (the GPIF data bus) are PB0-PB7, + * or PB0-PB7 + PD0-PD7 for 16-channel sampling. + * - Endpoint 2 (quad-buffered) is used for data transfers from FX2 to host. * * Documentation: * @@ -41,205 +40,16 @@ #include #include #include -#include #include #include #include - -#define SYNCDELAY() SYNCDELAY4 +#include +#include +#include /* ... */ -volatile bit got_sud; - -/* GPIF terminology: DP = decision point, NDP = non-decision-point */ - -/* - * GPIF waveforms. - * - * See section "10.3.4 State Instructions" in the TRM for details. - */ -static const BYTE wavedata[128] = { - /* Waveform 0: */ - - /* - * This is the basic algorithm implemented in our GPIF state machine: - * - * State 0: NDP: Sample the FIFO data bus. - * State 1: DP: If EP2 is full, go to state 7 (the IDLE state), i.e., - * end the current waveform. Otherwise, go to state 0 again, - * i.e., sample data until EP2 is full. - * State 2: Unused. - * State 3: Unused. - * State 4: Unused. - * State 5: Unused. - * State 6: Unused. - */ - - /* S0-S6: LENGTH/BRANCH */ - /* - * For NDPs (LENGTH): Number of IFCLK cycles to stay in this state. - * For DPs (BRANCH): [7] ReExec, [5:3]: BRANCHON1, [2:0]: BRANCHON0. - * - * 0x01: Stay one IFCLK cycle in this state. - * 0x38: No Re-execution, BRANCHON1 = state 7, BRANCHON0 = state 0. - */ - // 0x01, 0x38, 0x01, 0x01, 0x01, 0x01, 0x01, - // FIXME: For now just loop over the "sample data" state forever. - 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - /* TRM says "reserved", but GPIF designer always puts a 0x07 here. */ - 0x07, - - /* S0-S6: OPCODE */ - /* - * 0x02: NDP, sample the FIFO data bus. - * 0x01: DP, don't sample the FIFO data bus. - */ - 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - /* Reserved */ - 0x00, - - /* S0-S6: OUTPUT */ - /* Unused, we don't output anything, we only sample the pins. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* Reserved */ - 0x00, - - /* S0-S6: LOGIC FUNCTION (not used for NDPs) */ - /* - * 0x36: LFUNC = "A AND B", A = FIFO flag, B = FIFO flag. - * The FIFO flag (FF == full flag, in our case) is configured via - * EP2GPIFFLGSEL. - * - * So: If the EP2 FIFO is full and the EP2 FIFO is full, go to - * the state specified by BRANCHON1 (state 7), otherwise BRANCHON0 - * (state 0). See the LENGTH/BRANCH value above for details. - */ - 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, - /* TRM says "reserved", but GPIF designer always puts a 0x3f here. */ - 0x3f, - - /* TODO: Must unused waveforms be "valid"? */ - - /* Waveform 1 (unused): */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* Waveform 2 (unused): */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* Waveform 3 (unused): */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static void gpif_setup_registers(void) -{ - /* TODO. Value probably irrelevant, as we don't use RDY* signals? */ - GPIFREADYCFG = 0; - - /* - * Set TRICTL = 0, thus CTL0-CTL5 are CMOS outputs. - * TODO: Probably irrelevant, as we don't use CTL0-CTL5? - */ - GPIFCTLCFG = 0; - - /* When GPIF is idle, tri-state the data bus. */ - /* Bit 7: DONE, bit 0: IDLEDRV. TODO: Set/clear DONE bit? */ - GPIFIDLECS = (1 << 0); - - /* When GPIF is idle, set CTL0-CTL5 to 0. */ - GPIFIDLECTL = 0; - - /* - * Map index 0 in wavedata[] to FIFORD. The rest is assigned too, - * but not used by us. - * - * GPIFWFSELECT: [7:6] = SINGLEWR index, [5:4] = SINGLERD index, - * [3:2] = FIFOWR index, [1:0] = FIFORD index - */ - GPIFWFSELECT = (0x3 << 6) | (0x2 << 4) | (0x1 << 2) | (0x0 << 0); - - /* Contains RDY* pin values. Read-only according to TRM. */ - GPIFREADYSTAT = 0; -} - -static void gpif_write_waveforms(void) -{ - int i; - - /* - * Write the four waveforms into the respective WAVEDATA register - * locations (0xe400 - 0xe47f) using the FX2's autopointer feature. - */ - AUTOPTRSETUP = 0x07; /* Increment autopointers 1 & 2. */ - AUTOPTRH1 = MSB((WORD)wavedata); /* Source is the 'wavedata' array. */ - AUTOPTRL1 = LSB((WORD)wavedata); - AUTOPTRH2 = 0xe4; /* Dest is WAVEDATA (0xe400). */ - AUTOPTRL2 = 0x00; - for (i = 0; i < 128; i++) - EXTAUTODAT2 = EXTAUTODAT1; -} - -static void gpif_init_addr_pins(void) -{ - /* - * Configure the 9 GPIF address pins (GPIFADR[8:0], which consist of - * PORTC[7:0] and PORTE[7]), and output an initial address (zero). - * TODO: Probably irrelevant, the 56pin FX2 has no ports C and E. - */ - PORTCCFG = 0xff; /* Set PORTC[7:0] as alt. func. (GPIFADR[7:0]). */ - OEC = 0xff; /* Configure PORTC[7:0] as outputs. */ - PORTECFG |= 0x80; /* Set PORTE[7] as alt. func. (GPIFADR[8]). */ - OEE |= 0x80; /* Configure PORTE[7] as output. */ - SYNCDELAY(); - GPIFADRL = 0x00; /* Clear GPIFADR[7:0]. */ - SYNCDELAY(); - GPIFADRH = 0x00; /* Clear GPIFADR[8]. */ -} - -static void gpif_init_flowstates(void) -{ - /* Clear all flowstate registers, we don't use this functionality. */ - FLOWSTATE = 0; - FLOWLOGIC = 0; - FLOWEQ0CTL = 0; - FLOWEQ1CTL = 0; - FLOWHOLDOFF = 0; - FLOWSTB = 0; - FLOWSTBEDGE = 0; - FLOWSTBHPERIOD = 0; -} - -static void gpif_init_la(void) -{ - /* - * Setup the FX2 in GPIF master mode, using the internal clock - * (non-inverted) at 48MHz, and using async sampling. - */ - IFCONFIG = 0xee; - - /* Abort currently executing GPIF waveform (if any). */ - GPIFABORT = 0xff; - - /* Setup the GPIF registers. */ - gpif_setup_registers(); - - /* Write the four GPIF waveforms into the WAVEDATA register. */ - gpif_write_waveforms(); - - /* Initialize GPIF address pins, output initial values. */ - gpif_init_addr_pins(); - - /* Initialize flowstate registers (not used by us). */ - gpif_init_flowstates(); -} +volatile __bit got_sud; +BYTE vendor_command; static void setup_endpoints(void) { @@ -247,7 +57,7 @@ static void setup_endpoints(void) EP2CFG = (1 << 7) | /* EP is valid/activated */ (1 << 6) | /* EP direction: IN */ (1 << 5) | (0 << 4) | /* EP Type: bulk */ - (0 << 3) | /* EP buffer size: 512 */ + (1 << 3) | /* EP buffer size: 1024 */ (0 << 2) | /* Reserved. */ (0 << 1) | (0 << 0); /* EP buffering: quad buffering */ SYNCDELAY(); @@ -269,7 +79,7 @@ static void setup_endpoints(void) RESETFIFO(0x02) /* EP2: Enable AUTOIN mode. Set FIFO width to 8bits. */ - EP2FIFOCFG = bmAUTOIN | ~bmWORDWIDE; + EP2FIFOCFG = bmAUTOIN; SYNCDELAY(); /* EP2: Auto-commit 512 (0x200) byte packets (due to AUTOIN = 1). */ @@ -283,14 +93,49 @@ static void setup_endpoints(void) SYNCDELAY(); } -BOOL handle_vendorcommand(BYTE cmd) +static void send_fw_version(void) +{ + /* Populate the buffer. */ + struct version_info *const vi = (struct version_info *)EP0BUF; + vi->major = FX2LAFW_VERSION_MAJOR; + vi->minor = FX2LAFW_VERSION_MINOR; + + /* Send the message. */ + EP0BCH = 0; + EP0BCL = sizeof(struct version_info); +} + +static void send_revid_version(void) { - (void)cmd; + uint8_t *p; + + /* Populate the buffer. */ + p = (uint8_t *)EP0BUF; + *p = REVID; - /* - * TODO: Implement the protocol using control requests of type - * 'vendor-specific' (bmRequestType[6:5] = 2). - */ + /* Send the message. */ + EP0BCH = 0; + EP0BCL = 1; +} + +BOOL handle_vendorcommand(BYTE cmd) +{ + /* Protocol implementation */ + switch (cmd) { + case CMD_START: + vendor_command = cmd; + EP0BCL = 0; + return TRUE; + break; + case CMD_GET_FW_VERSION: + send_fw_version(); + return TRUE; + break; + case CMD_GET_REVID_VERSION: + send_revid_version(); + return TRUE; + break; + } return FALSE; } @@ -310,7 +155,7 @@ BOOL handle_set_interface(BYTE ifc, BYTE alt_ifc) /* We only support interface 0, alternate interface 0. */ if (ifc != 0 || alt_ifc != 0) return FALSE; - + /* Perform procedure from TRM, section 2.3.7: */ /* (1) TODO. */ @@ -341,35 +186,36 @@ BOOL handle_set_configuration(BYTE cfg) return (cfg == 1) ? TRUE : FALSE; } -void sudav_isr(void) interrupt SUDAV_ISR +void sudav_isr(void) __interrupt SUDAV_ISR { got_sud = TRUE; CLEAR_SUDAV(); } -void sof_isr(void) interrupt SOF_ISR using 1 +void sof_isr(void) __interrupt SOF_ISR __using 1 { CLEAR_SOF(); } -void usbreset_isr(void) interrupt USBRESET_ISR +void usbreset_isr(void) __interrupt USBRESET_ISR { handle_hispeed(FALSE); CLEAR_USBRESET(); } -void hispeed_isr(void) interrupt HISPEED_ISR +void hispeed_isr(void) __interrupt HISPEED_ISR { handle_hispeed(TRUE); CLEAR_HISPEED(); } -void main(void) +void fx2lafw_init(void) { /* Set DYN_OUT and ENH_PKT bits, as recommended by the TRM. */ REVCTL = bmNOAUTOARM | bmSKIPCOMMIT; got_sud = FALSE; + vendor_command = 0; /* Renumerate. */ RENUMERATE_UNCOND(); @@ -392,14 +238,42 @@ void main(void) /* Put the FX2 into GPIF master mode and setup the GPIF. */ gpif_init_la(); +} - /* Perform the initial GPIF read. */ - gpif_fifo_read(GPIF_EP2); +void fx2lafw_poll(void) +{ + if (got_sud) { + handle_setupdata(); + got_sud = FALSE; + } - while (1) { - if (got_sud) { - handle_setupdata(); - got_sud = FALSE; + if (vendor_command) { + switch (vendor_command) { + case CMD_START: + if ((EP0CS & bmEPBUSY) != 0) + break; + + if (EP0BCL == sizeof(struct cmd_start_acquisition)) { + gpif_acquisition_start( + (const struct cmd_start_acquisition *)EP0BUF); + } + + /* Acknowledge the vendor command. */ + vendor_command = 0; + break; + default: + /* Unimplemented command. */ + vendor_command = 0; + break; } } + + gpif_poll(); +} + +void main(void) +{ + fx2lafw_init(); + while (1) + fx2lafw_poll(); }