/*
- * This file is part of the fx2lafw project.
+ * This file is part of the sigrok-firmware-fx2lafw project.
*
* Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
*
* - 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:
*
#include <fx2regs.h>
#include <fx2macros.h>
+#include <fx2ints.h>
#include <delay.h>
-#include <autovector.h>
#include <setupdat.h>
#include <eputils.h>
#include <gpif.h>
+#include <command.h>
+#include <fx2lafw.h>
+#include <gpif-acquisition.h>
-/* Protocol commands */
-#define CMD_SET_SAMPLERATE 0xb0
-#define CMD_START 0xb1
-#define CMD_STOP 0xb2
-#define CMD_GET_FW_VERSION 0xb3
/* ... */
+volatile __bit got_sud;
+BYTE vendor_command;
-#define SYNCDELAY() SYNCDELAY4
-
-/* ... */
-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 WORD ledcounter = 0;
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();
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). */
SYNCDELAY();
}
+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;
+ SYNCDELAY();
+ EP0BCL = sizeof(struct version_info);
+ SYNCDELAY();
+}
+
+static void send_revid_version(void)
+{
+ uint8_t *p;
+
+ /* Populate the buffer. */
+ p = (uint8_t *)EP0BUF;
+ *p = REVID;
+
+ /* Send the message. */
+ EP0BCH = 0;
+ SYNCDELAY();
+ EP0BCL = 1;
+ SYNCDELAY();
+}
+
BOOL handle_vendorcommand(BYTE cmd)
{
/* Protocol implementation */
-
switch (cmd) {
- case CMD_SET_SAMPLERATE:
- /* TODO */
- break;
case CMD_START:
- /* TODO */
- break;
- case CMD_STOP:
- GPIFABORT = 0xff;
- /* TODO */
+ /* Tell hardware we are ready to receive data. */
+ vendor_command = cmd;
+ EP0BCL = 0;
+ SYNCDELAY();
return TRUE;
- break;
case CMD_GET_FW_VERSION:
- /* TODO */
- break;
- default:
- /* Unimplemented command. */
- break;
+ send_fw_version();
+ return TRUE;
+ case CMD_GET_REVID_VERSION:
+ send_revid_version();
+ return TRUE;
}
return FALSE;
/* 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. */
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
+/* IN BULK NAK - the host started requesting data. */
+void ibn_isr(void) __interrupt IBN_ISR
{
- CLEAR_SOF();
+ /*
+ * If the IBN interrupt is not disabled, clearing
+ * does not work. See AN78446, 6.2.
+ */
+ BYTE ibnsave = IBNIE;
+ IBNIE = 0;
+ CLEAR_USBINT();
+
+ /*
+ * If the host sent the START command, start the GPIF
+ * engine. The host will repeat the BULK IN in the next
+ * microframe.
+ */
+ if ((IBNIRQ & bmEP2IBN) && (gpif_acquiring == PREPARED)) {
+ ledcounter = 1;
+ PA1 = 0;
+ gpif_acquisition_start();
+ }
+
+ /* Clear IBN flags for all EPs. */
+ IBNIRQ = 0xff;
+
+ NAKIRQ = bmIBN;
+ SYNCDELAY();
+
+ IBNIE = ibnsave;
+ SYNCDELAY();
}
-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 timer2_isr(void) __interrupt TF2_ISR
+{
+ /* Blink LED during acquisition, keep it on otherwise. */
+ if (gpif_acquiring == RUNNING) {
+ if (--ledcounter == 0) {
+ PA1 = !PA1;
+ ledcounter = 1000;
+ }
+ } else if (gpif_acquiring == STOPPED) {
+ PA1 = 1; /* LED on. */
+ }
+ TF2 = 0;
+}
+
+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();
/* TODO: Does the order of the following lines matter? */
ENABLE_SUDAV();
- ENABLE_SOF();
+ ENABLE_EP2IBN();
ENABLE_HISPEED();
ENABLE_USBRESET();
+ /* PA1 (LED) is an output. */
+ PORTACFG = 0;
+ OEA = (1 << 1);
+ PA1 = 1; /* LED on. */
+
+ /* Init timer2. */
+ RCAP2L = -500 & 0xff;
+ RCAP2H = (-500 & 0xff00) >> 8;
+ T2CON = 0;
+ ET2 = 1;
+ TR2 = 1;
+
/* Global (8051) interrupt enable. */
EA = 1;
/* 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_prepare(
+ (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();
}