logicanalyzer/Firmware/LogicAnalyzer/LogicAnalyzer.pio

977 lines
37 KiB
Plaintext
Raw Normal View History

2022-07-03 14:07:25 +00:00
;--------------------------------------------------------------------------------------------
.program POSITIVE_CAPTURE
pull
out x 32 ;read capture length
pull
out y 32 ;read end mark
.wrap_target
in pins 32 ;read sample
jmp pin POST_CAPTURE ;exit wrap if pin is set
.wrap
POST_CAPTURE:
in pins 32 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed
in Y 32 ;write end mark
irq 0 ;notify to the main program that we have finished capturing
LOCK:
jmp LOCK ;block the program
;--------------------------------------------------------------------------------------------
.program NEGATIVE_CAPTURE
.wrap_target
pull
out x 32 ;read capture length
pull
out y 32 ;read end mark
PRE_CAPTURE:
in pins 32 ;read sample
jmp pin PRE_CAPTURE ;loop if pin is set
POST_CAPTURE:
in pins 32 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed
in Y 32 ;write end mark
irq 0 ;notify to the main program that we have finished capturing
LOCK:
jmp LOCK ;block the program
.wrap
;--------------------------------------------------------------------------------------------
.program COMPLEX_CAPTURE
pull
out x 32 ;read capture length
pull
out y 32 ;read end mark
wait irq 7 ;wait for trigger program to be ready
.wrap_target
in pins 29 ;read sample
jmp pin POST_CAPTURE ;exit wrap if pin is set
.wrap
POST_CAPTURE:
in pins 29 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed
in Y 32 ;write end mark
irq 0 ;notify to the main program that we have finished capturing
LOCK:
jmp LOCK ;block the program
;--------------------------------------------------------------------------------------------
.program FAST_CAPTURE
pull
out x 32 ;read capture length
pull
out y 32 ;read end mark
.wrap_target
in pins 29 ;read sample
jmp pin POST_CAPTURE ;exit wrap if pin is set
.wrap
POST_CAPTURE:
in pins 29 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed
in Y 32 ;write end mark
irq 0 ;notify to the main program that we have finished capturing
LOCK:
jmp LOCK ;block the program
;--------------------------------------------------------------------------------------------
;--------Kept only for reference, the program is stored in volatile memory as it must--------
;---------be modified for concrete trigger parameters.---------------------------------------
;--------------------------------------------------------------------------------------------
;.program COMPLEX_TRIGGER
; pull
; out x 32 ;read trigger value
; set pins 0 ;set trigger pin to low
; irq 7 ;Release capture program
;TRIGGER_LOOP:
; mov osr, pins ;read pin status to output shift register
; out y, 4 ;output 4 bits to Y (writes 32 bits)
; jmp x!=y TRIGGER_LOOP ;loop if trigger not met
; set pins 1 ;set trigger pin to high (trigger met)
;LOCK:
; jmp LOCK ;block program
% c-sdk {
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "string.h"
//Static variables for the PIO programs
static PIO capturePIO;
static PIO triggerPIO;
static uint sm_Capture;
static uint captureOffset;
static uint sm_Trigger;
static uint triggerOffset;
//Static variables for DMA channels
static uint32_t dmaPingPong0;
static uint32_t dmaPingPong1;
static uint32_t dmaPingPong2;
static uint32_t dmaPingPong3;
//Static information of the last capture
static uint8_t lastCapturePins[24]; //List of captured pins
static uint8_t lastCapturePinCount; //Count of captured pins
static uint32_t lastTriggerCapture; //Moment where the trigger happened inside the circular pre buffer
static uint32_t lastPreSize; //Pre-trigger buffer size
static uint32_t lastPostSize; //Post-trigger buffer size
static bool lastTriggerInverted; //Inverted?
static uint8_t lastTriggerPin;
static uint32_t lastStartPosition;
static bool lastCaptureComplexFast;
static uint8_t lastTriggerPinBase;
static uint32_t lastTriggerPinCount;
//Static information of the current capture
static bool captureFinished;
static bool captureProcessed;
//Pin mapping, used to map the channels to the PIO program
//Could be stored into flash memory but it causes problems
const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28};
//Main capture buffer, aligned at a 32k boundary, to use the maxixmum ring size supported by DMA channels
static uint32_t captureBuffer[32 * 1024] __attribute__((aligned(32768)));
//-----------------------------------------------------------------------------
//--------------Complex trigger PIO program------------------------------------
//-----------------------------------------------------------------------------
#define COMPLEX_TRIGGER_wrap_target 0
#define COMPLEX_TRIGGER_wrap 8
uint16_t COMPLEX_TRIGGER_program_instructions[] = {
// .wrap_target
0x80a0, // 0: pull block
0x6020, // 1: out x, 32
0xe000, // 2: set pins, 0
0xc007, // 3: irq nowait 7
0xa0e0, // 4: mov osr, pins
0x6044, // 5: out y, 4
0x00a4, // 6: jmp x != y, 4
0xe001, // 7: set pins, 1
0x0008, // 8: jmp 8
// .wrap
};
struct pio_program COMPLEX_TRIGGER_program = {
.instructions = COMPLEX_TRIGGER_program_instructions,
.length = 9,
.origin = -1,
};
static inline pio_sm_config COMPLEX_TRIGGER_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + COMPLEX_TRIGGER_wrap_target, offset + COMPLEX_TRIGGER_wrap);
return c;
}
//-----------------------------------------------------------------------------
//--------------Complex trigger PIO program END--------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//--------------Fast trigger PIO program---------------------------------------
//-----------------------------------------------------------------------------
#define FAST_TRIGGER_wrap_target 0
#define FAST_TRIGGER_wrap 31
uint16_t FAST_TRIGGER_program_instructions[32];
struct pio_program FAST_TRIGGER_program = {
.instructions = FAST_TRIGGER_program_instructions,
.length = 32,
.origin = 0,
};
static inline pio_sm_config FAST_TRIGGER_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + FAST_TRIGGER_wrap_target, offset + FAST_TRIGGER_wrap);
sm_config_set_sideset(&c, 1, false, false);
return c;
}
2022-07-11 18:03:50 +00:00
uint8_t create_fast_trigger_program(uint8_t pattern, uint8_t length)
2022-07-03 14:07:25 +00:00
{
//This creates a 32 instruction jump table. Each instruction is a MOV PC, PINS except for the addresses that
//match the specified pattern.
uint8_t i;
uint8_t mask = (1 << length) - 1; //Mask for testing address vs pattern
2022-07-11 18:03:50 +00:00
uint8_t first = 255;
2022-07-03 14:07:25 +00:00
for(i = 0; i < 32; i++)
{
if((i & mask) == pattern)
FAST_TRIGGER_program_instructions[i] = 0x1000 | i; //JMP i SIDE 1
else
2022-07-11 18:03:50 +00:00
{
2022-07-03 14:07:25 +00:00
FAST_TRIGGER_program_instructions[i] = 0xA0A0; //MOV PC, PINS SIDE 0
2022-07-11 18:03:50 +00:00
first = i;
}
2022-07-03 14:07:25 +00:00
}
2022-07-11 18:03:50 +00:00
return first;
2022-07-03 14:07:25 +00:00
}
//-----------------------------------------------------------------------------
//--------------Fast trigger PIO program END-----------------------------------
//-----------------------------------------------------------------------------
2022-07-11 18:03:50 +00:00
2022-07-03 14:07:25 +00:00
void fast_capture_completed()
{
//TODO: REWRITE
//Abort DMA channels
dma_channel_abort(dmaPingPong0);
dma_channel_abort(dmaPingPong1);
dma_channel_abort(dmaPingPong2);
dma_channel_abort(dmaPingPong3);
//Clear PIO interrupt 0 and unhook handler
pio_interrupt_clear(capturePIO, 0);
irq_set_enabled(PIO0_IRQ_0, false);
irq_set_enabled(pio_get_dreq(capturePIO, sm_Capture, false), false);
irq_remove_handler(PIO0_IRQ_0, fast_capture_completed);
//Disable all DMA channels
dma_channel_config c = dma_channel_get_default_config(dmaPingPong0);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong0, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong0);
c = dma_channel_get_default_config(dmaPingPong1);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong1, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong1);
c = dma_channel_get_default_config(dmaPingPong2);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong2, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong2);
c = dma_channel_get_default_config(dmaPingPong3);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong3, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong3);
//Stop PIO capture program and clear
pio_sm_set_enabled(capturePIO, sm_Capture, false);
pio_sm_unclaim(capturePIO, sm_Capture);
pio_remove_program(capturePIO, &FAST_CAPTURE_program, captureOffset);
//Stop PIO trigger program and clear
pio_sm_set_enabled(triggerPIO, sm_Trigger, false);
pio_sm_set_pins(triggerPIO, sm_Trigger, 0);
pio_sm_unclaim(triggerPIO, sm_Trigger);
pio_remove_program(triggerPIO, &FAST_TRIGGER_program, triggerOffset);
//Mark the capture as finished
captureFinished = true;
}
void complex_capture_completed()
{
//Abort DMA channels
dma_channel_abort(dmaPingPong0);
dma_channel_abort(dmaPingPong1);
dma_channel_abort(dmaPingPong2);
dma_channel_abort(dmaPingPong3);
//Clear PIO interrupt 0 and unhook handler
pio_interrupt_clear(capturePIO, 0);
irq_set_enabled(PIO0_IRQ_0, false);
irq_set_enabled(pio_get_dreq(capturePIO, sm_Capture, false), false);
irq_remove_handler(PIO0_IRQ_0, complex_capture_completed);
//Disable all DMA channels
dma_channel_config c = dma_channel_get_default_config(dmaPingPong0);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong0, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong0);
c = dma_channel_get_default_config(dmaPingPong1);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong1, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong1);
c = dma_channel_get_default_config(dmaPingPong2);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong2, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong2);
c = dma_channel_get_default_config(dmaPingPong3);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong3, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong3);
//Stop PIO capture program and clear
pio_sm_set_enabled(capturePIO, sm_Capture, false);
pio_sm_unclaim(capturePIO, sm_Capture);
pio_remove_program(capturePIO, &COMPLEX_CAPTURE_program, captureOffset);
//Stop PIO trigger program and clear
pio_sm_set_enabled(capturePIO, sm_Trigger, false);
pio_sm_set_pins(capturePIO, sm_Trigger, 0);
pio_sm_unclaim(capturePIO, sm_Trigger);
pio_remove_program(capturePIO, &COMPLEX_TRIGGER_program, triggerOffset);
//Mark the capture as finished
captureFinished = true;
}
void simple_capture_completed()
{
//Abort DMA channels
dma_channel_abort(dmaPingPong0);
dma_channel_abort(dmaPingPong1);
dma_channel_abort(dmaPingPong2);
dma_channel_abort(dmaPingPong3);
//Clear PIO interrupt 0 and unhook handler
pio_interrupt_clear(capturePIO, 0);
irq_set_enabled(PIO0_IRQ_0, false);
irq_set_enabled(pio_get_dreq(capturePIO, sm_Capture, false), false);
irq_remove_handler(PIO0_IRQ_0, simple_capture_completed);
//Disable and unclaim all DMA channels
dma_channel_config c = dma_channel_get_default_config(dmaPingPong0);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong0, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong0);
c = dma_channel_get_default_config(dmaPingPong1);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong1, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong1);
c = dma_channel_get_default_config(dmaPingPong2);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong2, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong2);
c = dma_channel_get_default_config(dmaPingPong3);
channel_config_set_enable(&c, false);
dma_channel_configure(dmaPingPong3, &c, 0, 0, 0, false);
dma_channel_unclaim(dmaPingPong3);
//Stop PIO program and clear
pio_sm_set_enabled(capturePIO, sm_Capture, false);
pio_sm_unclaim(capturePIO, sm_Capture);
if(lastTriggerInverted)
pio_remove_program(capturePIO, &POSITIVE_CAPTURE_program, captureOffset);
else
pio_remove_program(capturePIO, &NEGATIVE_CAPTURE_program, captureOffset);
//Mark the capture as finished
captureFinished = true;
}
void configureCaptureDMAs()
{
//Claim four DMA channels, each channel writes to 32Kb of the buffer (8192 samples) as that's the maximum ring size supported
dmaPingPong0 = dma_claim_unused_channel(true);
dmaPingPong1 = dma_claim_unused_channel(true);
dmaPingPong2 = dma_claim_unused_channel(true);
dmaPingPong3 = dma_claim_unused_channel(true);
//Configure first capture DMA
dma_channel_config dmaPingPong0Config = dma_channel_get_default_config(dmaPingPong0);
channel_config_set_read_increment(&dmaPingPong0Config, false); //Do not increment read address
channel_config_set_write_increment(&dmaPingPong0Config, true); //Increment write address
channel_config_set_transfer_data_size(&dmaPingPong0Config, DMA_SIZE_32); //Transfer 32 bits each time
channel_config_set_chain_to(&dmaPingPong0Config, dmaPingPong1); //Chain to the second pre-trigger dma channel
channel_config_set_dreq(&dmaPingPong0Config, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO
channel_config_set_ring(&dmaPingPong0Config, true, 15); //Ring at 32768 bytes
//Configure second capture DMA
dma_channel_config dmaPingPong1Config = dma_channel_get_default_config(dmaPingPong1);
channel_config_set_read_increment(&dmaPingPong1Config, false); //Do not increment read address
channel_config_set_write_increment(&dmaPingPong1Config, true); //Increment write address
channel_config_set_transfer_data_size(&dmaPingPong1Config, DMA_SIZE_32); //Transfer 32 bits each time
channel_config_set_chain_to(&dmaPingPong1Config, dmaPingPong2); //Chain to the third pre-trigger dma channel
channel_config_set_dreq(&dmaPingPong1Config, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO
channel_config_set_ring(&dmaPingPong1Config, true, 15); //Ring at 32768 bytes
//Configure third capture DMA
dma_channel_config dmaPingPong2Config = dma_channel_get_default_config(dmaPingPong2);
channel_config_set_read_increment(&dmaPingPong2Config, false); //Do not increment read address
channel_config_set_write_increment(&dmaPingPong2Config, true); //Increment write address
channel_config_set_transfer_data_size(&dmaPingPong2Config, DMA_SIZE_32); //Transfer 32 bits each time
channel_config_set_chain_to(&dmaPingPong2Config, dmaPingPong3); //Chain to the fourth pre-trigger dma channel
channel_config_set_dreq(&dmaPingPong2Config, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO
channel_config_set_ring(&dmaPingPong2Config, true, 15); //Ring at 32768 bytes
//Configure fourth capture DMA
dma_channel_config dmaPingPong3Config = dma_channel_get_default_config(dmaPingPong3);
channel_config_set_read_increment(&dmaPingPong3Config, false); //Do not increment read address
channel_config_set_write_increment(&dmaPingPong3Config, true); //Increment write address
channel_config_set_transfer_data_size(&dmaPingPong3Config, DMA_SIZE_32); //Transfer 32 bits each time
channel_config_set_chain_to(&dmaPingPong3Config, dmaPingPong0); //Chain to the first pre-trigger dma channel
channel_config_set_dreq(&dmaPingPong3Config, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO
channel_config_set_ring(&dmaPingPong3Config, true, 15); //Ring at 32768 bytes
//Configure the DMA channels
dma_channel_configure(dmaPingPong3, &dmaPingPong3Config, &captureBuffer[24 * 1024], &capturePIO->rxf[sm_Capture], 8192, false); //Configure the channel
dma_channel_configure(dmaPingPong2, &dmaPingPong2Config, &captureBuffer[16 * 1024], &capturePIO->rxf[sm_Capture], 8192, false); //Configure the channel
dma_channel_configure(dmaPingPong1, &dmaPingPong1Config, &captureBuffer[8 * 1024], &capturePIO->rxf[sm_Capture], 8192, false); //Configure the channel
dma_channel_configure(dmaPingPong0, &dmaPingPong0Config, &captureBuffer[0], &capturePIO->rxf[sm_Capture], 8192, true);
}
bool startCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue)
{
//ABOUT THE FAST TRIGGER
//
//The fast trigger is an evolution of the complex trigger.
//Like the complex trigger this is a sepparate program that checks for a pattern to trigger the capture program second stage.
//
//The main difference is the maximum length of the pattern to match and the sampling speed. This fast trigger
//can only use a pattern up to 5 bits, but it captures at maximum speed of 100Msps (it could even sample up to 200Mhz but to match the
//maximum speed of the sampling it is limited to 100Msps).
//To achieve this the program occupies all 32 instructions of a PIO module, this is basically a jump table, each
//instruction moves the pin values to the program counter except for the ones that match the pattern, which activate the
//trigger pin using the side pins and create an infinite loop jumping to itself (basically a JMP currentpc SIDE 1).
//
//This solves the speed and latency problem, the speed reaches 100Msps and the latency is reduced to a maximum of 2 cycles, but
//still can glitch on low speeds and also occupies a complete PIO module (but we have one unused, so its not a problem)
//Too many samples requested?
if(preLength + postLength >= (32 * 1024))
return false;
//Frequency too high?
if(freq > 100000000)
return false;
//Incorrect pin count?
if(capturePinCount < 0 || capturePinCount > 24)
return false;
//Bad trigger?
if(triggerPinBase > 15 || triggerPinCount > 5 || triggerPinCount < 1 || triggerPinCount + triggerPinBase > 16)
return false;
//Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled)
memset(captureBuffer, 0x00, sizeof(captureBuffer));
//Store info about the capture
lastPreSize = preLength;
lastPostSize = postLength;
lastCapturePinCount = capturePinCount;
lastCaptureComplexFast = true;
//Map channels to pins
for(uint8_t i = 0; i < capturePinCount; i++)
lastCapturePins[i] = pinMap[capturePins[i]];
//Store trigger info
triggerPinBase = pinMap[triggerPinBase];
lastTriggerPinBase = triggerPinBase;
//Calculate clock divider based on frequency, it generates a clock 2x faster than the capture freequency
float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq * 2);
//Store the PIO units and clear program memory
capturePIO = pio0;
pio_clear_instruction_memory(capturePIO);
triggerPIO = pio1;
pio_clear_instruction_memory(triggerPIO);
//Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins)
pio_gpio_init(triggerPIO, 0);
pio_gpio_init(capturePIO, 1);
2022-07-11 18:03:50 +00:00
for(uint8_t i = 0; i < capturePinCount; i++)
2022-07-03 14:07:25 +00:00
pio_gpio_init(capturePIO, lastCapturePins[i]);
//Configure capture SM
sm_Capture = pio_claim_unused_sm(capturePIO, true);
pio_sm_clear_fifos(capturePIO, sm_Capture);
pio_sm_restart(capturePIO, sm_Capture);
captureOffset = pio_add_program(capturePIO, &FAST_CAPTURE_program);
//Input pins start at pin 2, 29 pins are captured
pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, 2, 29, false);
//Configure state machines
pio_sm_config smConfig = FAST_CAPTURE_program_get_default_config(captureOffset);
//Inputs start at pin 2
sm_config_set_in_pins(&smConfig, 2);
//Set clock to 2x required frequency
sm_config_set_clkdiv(&smConfig, clockDiv);
//Autopush per 29 bits
sm_config_set_in_shift(&smConfig, false, true, 29);
//Configure fast trigger pin (pin 1) as JMP pin.
sm_config_set_jmp_pin(&smConfig, 1);
//Configure interrupt 0
pio_interrupt_clear (capturePIO, 0);
pio_set_irq0_source_enabled(capturePIO, pis_interrupt0, true);
irq_set_exclusive_handler(PIO0_IRQ_0, fast_capture_completed);
irq_set_enabled(PIO0_IRQ_0, true);
irq_set_enabled(pio_get_dreq(capturePIO, sm_Capture, false), true);
//Initialize state machine
pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig);
//Configure trigger SM
sm_Trigger = pio_claim_unused_sm(triggerPIO, true);
pio_sm_clear_fifos(triggerPIO, sm_Trigger);
pio_sm_restart(triggerPIO, sm_Trigger);
//Create trigger program
2022-07-11 18:03:50 +00:00
uint8_t triggerFirstInstruction = create_fast_trigger_program(triggerValue, triggerPinCount);
2022-07-03 14:07:25 +00:00
//Configure trigger state machine
triggerOffset = pio_add_program(triggerPIO, &FAST_TRIGGER_program);
pio_sm_set_consecutive_pindirs(triggerPIO, sm_Trigger, 0, 1, true); //Pin 0 as output (connects to Pin 1, to trigger capture)
pio_sm_set_consecutive_pindirs(triggerPIO, sm_Trigger, triggerPinBase, triggerPinCount, false); //Trigger pins start at triggerPinBase
smConfig = FAST_TRIGGER_program_get_default_config(triggerOffset);
sm_config_set_in_pins(&smConfig, triggerPinBase); //Trigger input starts at pin base
2022-07-11 18:03:50 +00:00
sm_config_set_set_pins(&smConfig, 0, 1); //Trigger output is a set pin
2022-07-03 14:07:25 +00:00
sm_config_set_sideset_pins(&smConfig, 0); //Trigger output is a side pin
sm_config_set_clkdiv(&smConfig, 1); //Trigger always runs at half speed (100Msps)
//Configure DMA's
configureCaptureDMAs();
//Enable capture state machine
pio_sm_set_enabled(capturePIO, sm_Capture, true);
//Write capture length to post program
pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1);
//Write capture end mark to post program
pio_sm_put_blocking(capturePIO, sm_Capture, 0xFFFFFFFF);
//Initialize trigger state machine
pio_sm_init(triggerPIO, sm_Trigger, triggerOffset, &smConfig);
//Enable trigger state machine
pio_sm_set_enabled(triggerPIO, sm_Trigger, true);
//Finally clear capture status and process flags
captureFinished = false;
captureProcessed = false;
//We're done
return true;
}
bool startCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue)
{
//ABOUT THE COMPLEX TRIGGER
//
//The complex trigger is a hack to achieve the maximum speed in the capture program.
//To get to 100Msps with a 200Mhz clock each capture must be excuted in two instructions. For this the basic
//capture programs (the positive and negative ones) use the JMP PIN instruction, this redirects the program flow based in the
//state of a pin, so with an IN instruction and a JMP instruction we can create a loop that captures data until the trigger pin
//is in the correct edge and then jumps to another subroutine that captures until the post-trigger samples are met.
//
//Unfortunately there is no way to jump to a subroutine based in the status of more than one pin, you can jump based in the
//comparison of the scratch registers, but this requires more than one instruction to prepare the data.
//So, what I have implemented here is an asynchronouss trigger, a second state machine running at máximum speed checks if the trigger
//condition is met and then notifies to the first state machine. But... there is no way to notify of something between state machines
//except for interrupts, and interrupts blocks the code execution (you WAIT for the interrupt) so this is not viable, so we use a hack, we
//interconnect two pins (GPIO0 and GPIO1), one is an output from the trigger state machine and the other is the JMP PIN for the capture
//state machine. When the trigger condition is met the output pin is set to 1 so the JMP PIN pin receives this signal and we can keep
//our capture program to use two instructions.
//This carries some limitations, the trigger can only work up to 66Msps but the capture can go up to 100Msps as they are independent.
//Also, as the trigger always runs at maximum speed there may happen a glitch in the trigger signal for lower capture speeds, the
//condition may be met but for less time than a capture cycle, so the capture machine will not sample this trigger condition.
//Finally the trigger also has some cycles of delay, 3 instructions plus 2 cycles of propagation to the ISR, so a maximum of
//25ns of delay can happen.
//Too many samples requested?
if(preLength + postLength >= (32 * 1024))
return false;
//Frequency too high?
if(freq > 100000000)
return false;
//Incorrect pin count?
if(capturePinCount < 0 || capturePinCount > 24)
return false;
//Bad trigger?
if(triggerPinBase > 15 || triggerPinCount > 16 || triggerPinCount < 1 || triggerPinCount + triggerPinBase > 16)
return false;
//Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled)
memset(captureBuffer, 0x00, sizeof(captureBuffer));
//Store info about the capture
lastPreSize = preLength;
lastPostSize = postLength;
lastCapturePinCount = capturePinCount;
lastCaptureComplexFast = true;
//Map channels to pins
for(uint8_t i = 0; i < capturePinCount; i++)
lastCapturePins[i] = pinMap[capturePins[i]];
//Store trigger info
triggerPinBase = pinMap[triggerPinBase];
lastTriggerPinBase = triggerPinBase;
//Calculate clock divider based on frequency, it generates a clock 2x faster than the capture freequency
float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq * 2);
//Store the PIO unit and clear program memory
capturePIO = pio0;
pio_clear_instruction_memory(capturePIO);
//Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins)
pio_gpio_init(capturePIO, 0);
pio_gpio_init(capturePIO, 1);
2022-07-11 18:03:50 +00:00
for(uint8_t i = 0; i < capturePinCount; i++)
2022-07-03 14:07:25 +00:00
pio_gpio_init(capturePIO, lastCapturePins[i]);
//Configure capture SM
sm_Capture = pio_claim_unused_sm(capturePIO, true);
pio_sm_clear_fifos(capturePIO, sm_Capture);
pio_sm_restart(capturePIO, sm_Capture);
captureOffset = pio_add_program(capturePIO, &COMPLEX_CAPTURE_program);
//Input pins start at pin 2, 29 pins are captured
pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, 2, 29, false);
//Configure state machines
pio_sm_config smConfig = COMPLEX_CAPTURE_program_get_default_config(captureOffset);
//Inputs start at pin 2
sm_config_set_in_pins(&smConfig, 2);
//Set clock to 2x required frequency
sm_config_set_clkdiv(&smConfig, clockDiv);
//Autopush per 29 bits
sm_config_set_in_shift(&smConfig, false, true, 29);
//Configure comples trigger pin (pin 1) as JMP pin.
sm_config_set_jmp_pin(&smConfig, 1);
//Configure interrupt 0
pio_interrupt_clear (capturePIO, 0);
pio_set_irq0_source_enabled(capturePIO, pis_interrupt0, true);
irq_set_exclusive_handler(PIO0_IRQ_0, complex_capture_completed);
irq_set_enabled(PIO0_IRQ_0, true);
irq_set_enabled(pio_get_dreq(capturePIO, sm_Capture, false), true);
//Initialize state machine
pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig);
//Configure trigger SM
sm_Trigger = pio_claim_unused_sm(capturePIO, true);
pio_sm_clear_fifos(capturePIO, sm_Trigger);
pio_sm_restart(capturePIO, sm_Trigger);
//Modify trigger program to use the correct pins
COMPLEX_TRIGGER_program_instructions[5] = 0x6040 | triggerPinCount;
//Configure trigger state machine
triggerOffset = pio_add_program(capturePIO, &COMPLEX_TRIGGER_program);
pio_sm_set_consecutive_pindirs(capturePIO, sm_Trigger, 0, 1, true); //Pin 0 as output (connects to Pin 1, to trigger capture)
pio_sm_set_consecutive_pindirs(capturePIO, sm_Trigger, triggerPinBase, triggerPinCount, false); //Trigger pins start at triggerPinBase
smConfig = COMPLEX_TRIGGER_program_get_default_config(triggerOffset);
sm_config_set_in_pins(&smConfig, triggerPinBase); //Trigger input starts at pin base
sm_config_set_set_pins(&smConfig, 0, 1); //Trigger output is a set pin
sm_config_set_clkdiv(&smConfig, 1); //Trigger always runs at max speed
sm_config_set_in_shift(&smConfig, false, false, 0); //Trigger shifts left to right
//Initialize trigger state machine
pio_sm_init(capturePIO, sm_Trigger, triggerOffset, &smConfig); //Init trigger
//Configure DMA's
configureCaptureDMAs();
//Enable capture state machine
pio_sm_set_enabled(capturePIO, sm_Capture, true);
//Write capture length to post program
pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1);
//Write capture end mark to post program
pio_sm_put_blocking(capturePIO, sm_Capture, 0xFFFFFFFF);
//Enable trigger state machine
pio_sm_set_enabled(capturePIO, sm_Trigger, true);
//Write trigger value to trigger program
pio_sm_put_blocking(capturePIO, sm_Trigger, triggerValue);
//Finally clear capture status and process flags
captureFinished = false;
captureProcessed = false;
//We're done
return true;
}
bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger)
{
//Too many samples requested?
if(preLength + postLength >= (32 * 1024))
return false;
//Frequency too high?
if(freq > 100000000)
return false;
//Incorrect pin count?
if(capturePinCount < 0 || capturePinCount > 24)
return false;
//Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled)
memset(captureBuffer, 0x00, sizeof(captureBuffer));
//Store info about the capture
lastPreSize = preLength;
lastPostSize = postLength;
lastCapturePinCount = capturePinCount;
lastTriggerInverted = invertTrigger;
lastCaptureComplexFast = false;
//Map channels to pins
for(uint8_t i = 0; i < capturePinCount; i++)
lastCapturePins[i] = pinMap[capturePins[i]];
//Store trigger info
triggerPin = pinMap[triggerPin];
lastTriggerPin = triggerPin;
//Calculate clock divider based on frequency, it generates a clock 2x faster than the capture freequency
float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq * 2);
//Store the PIO unit and clear program memory
capturePIO = pio0;
pio_clear_instruction_memory(capturePIO);
//Configure capture SM
sm_Capture = pio_claim_unused_sm(capturePIO, true);
pio_sm_clear_fifos(capturePIO, sm_Capture);
pio_sm_restart(capturePIO, sm_Capture);
//Load correct program, depending on the trigger edge
if(invertTrigger)
captureOffset = pio_add_program(capturePIO, &NEGATIVE_CAPTURE_program);
else
captureOffset = pio_add_program(capturePIO, &POSITIVE_CAPTURE_program);
//Configure pins
pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, 0, 32, false);
pio_gpio_init(capturePIO, triggerPin);
for(uint8_t i = 0; i < capturePinCount; i++)
pio_gpio_init(capturePIO, lastCapturePins[i]);
//Configure state machines
pio_sm_config smConfig = invertTrigger?
NEGATIVE_CAPTURE_program_get_default_config(captureOffset):
POSITIVE_CAPTURE_program_get_default_config(captureOffset);
//All pins are inputs
sm_config_set_in_pins(&smConfig, 0);
//Set clock to 2x required frequency
sm_config_set_clkdiv(&smConfig, clockDiv);
//Autopush per dword
sm_config_set_in_shift(&smConfig, true, true, 0);
//Configure trigger pin as JMP pin.
sm_config_set_jmp_pin(&smConfig, triggerPin);
//Configure interupt 0
pio_interrupt_clear (capturePIO, 0);
pio_set_irq0_source_enabled(capturePIO, pis_interrupt0, true);
irq_set_exclusive_handler(PIO0_IRQ_0, simple_capture_completed);
irq_set_enabled(PIO0_IRQ_0, true);
irq_set_enabled(pio_get_dreq(capturePIO, sm_Capture, false), true);
//Initialize state machine
pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig);
configureCaptureDMAs();
//Enabl state machine
pio_sm_set_enabled(capturePIO, sm_Capture, true);
//Write capture length to post program to start the capture process
pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1);
//Write capture end mark to start capture
pio_sm_put_blocking(capturePIO, sm_Capture, 0xFFFFFFFF);
//Finally clear capture status and process flags
captureFinished = false;
captureProcessed = false;
//We're done
return true;
}
bool IsCapturing()
{
//If you need an explanation of this, you're a fool. :P
return !captureFinished;
}
uint32_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample)
{
//If we don't have processed the buffer...
if(!captureProcessed)
{
//Find capture end mark
int32_t lastCapture = 0;
for(int buc = 0; buc < 32768; buc++)
{
if(captureBuffer[buc] == 0xFFFFFFFF)
{
lastCapture = buc - 1;
if(lastCapture < 0)
lastCapture = 32767;
break;
}
}
//Calculate the first sample index
if(lastCapture < lastPreSize + lastPostSize - 1)
lastStartPosition = 32768 - ((lastPreSize + lastPostSize) - (lastCapture - 1));
else
lastStartPosition = lastCapture - (lastPreSize + lastPostSize) + 1;
uint32_t oldValue;
uint32_t newValue;
uint32_t currentPos = lastStartPosition;
//Sort channels
//(reorder captured bits based on the channels requested)
if(lastCaptureComplexFast) //Was this a fast/complex capture?
{
for(int buc = 0; buc < lastPreSize + lastPostSize; buc++)
{
oldValue = captureBuffer[currentPos]; //Store current value
newValue = 0; //New value
for(int pin = 0; pin < lastCapturePinCount; pin++) //For each captured channel...
newValue |= (oldValue & (1 << (lastCapturePins[pin] - 2))) >> ((lastCapturePins[pin] - 2) - pin); //Store its value in the correct bit
//Update value in the buffer
captureBuffer[currentPos++] = newValue;
//If we reached the end of the buffer, wrap around
if(currentPos >= 32768)
currentPos = 0;
}
}
else
{
//Same as for complex/fasst capture but without skipping the two first bits
for(int buc = 0; buc < lastPreSize + lastPostSize; buc++)
{
oldValue = captureBuffer[currentPos];
newValue = 0;
for(int pin = 0; pin < lastCapturePinCount; pin++)
newValue |= (oldValue & (1 << lastCapturePins[pin])) >> (lastCapturePins[pin] - pin);
captureBuffer[currentPos++] = newValue;
if(currentPos >= 32768)
currentPos = 0;
}
}
captureProcessed = true;
}
//Return data
*bufferSize = lastPreSize + lastPostSize;
*firstSample = lastStartPosition;
return captureBuffer;
}
%}