element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Open Source Hardware
  • Technologies
  • More
Open Source Hardware
Blog ARMP ADC, DAC and FPGA
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Open Source Hardware to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: michaelkellett
  • Date Created: 7 Aug 2020 4:19 PM Date Created
  • Views 1672 views
  • Likes 4 likes
  • Comments 4 comments
Related
Recommended

ARMP ADC, DAC and FPGA

michaelkellett
michaelkellett
7 Aug 2020

Overview

 

Once again there are few (if any) component vlaues on the schematic, these will appear in the BOM later on.

Treat the pin numbering on the FPFA with caution, I shall swap pins as appropriate when I layout the board.

 

image

 

The Details

 

The DAC digital interface is forced to SPI mode by pulling pin SPI2C low.

The FPGA drives it s three SPI signals (the DAC can't return any data to the FPGA.

The analogue signal from the DAC is low pass filtered by the Sallen and Key filter

based on U9. We can easily over sample the DAC drive by a large margin so

we don't need a fancy filter.

 

The ADC has a few more options:

The Rate pin is pulled high so that the sample rate =  SCKI / 512.

The S/M pin is pulled high so that the data interface operates in slave mode (ie is controlled by the FPGA).

SCKI is the primary clock for the ADC and is divided to make the acqusition clock, in many cases it would be

bad practice to drive it from the FPGA because FPGA outputs are rather jittery. Because we are only operating at

very low frequencies the FPGA levels of jitter won't cause a problem.

The FPGA also controls the BCK (data bit clock), the DATA and FSYNC pins. (Read the PCM4201 data sheet

for a full explanation. BCK and SCKI must be synchronised, so that SCKI is an exact multiple of BCK - no problem

for the FPGA.

All the signals between FPGA and the DAC and ADC are also available on J1. This is for debugging. I'm

assuming that people will want to tinker with this design and even re-purpose it - so I'm trying to make it easy

for them. J1 is a 0.1" pitch header - OK for debugging but to keep the size down no provision for locking connectors.

The input to the ADC has already been low pass filtered and because the ADC is  a sigma delta type

it doesn't need an exotic anti alias filter, having its own built in oversampling.

The ADC's reset and high pass filter enable are not connected to the FPGA but will be controlled directly

by the micro.

 

A 16MHz oscillator is connected to an FPGA clock input and will (probably) be up-converted by the PLL

to make the FPGA internal clock.

 

J2 is a Molex Picoblade connector to allow the FPGA to eb connected to the operator controls.

At present these are not defined. The 8 connections to the FPGA are sufficient for 8 pushbuttons or a couple

rotary encoders and two buttons or even a serial interface to shift registers or other input devices.

I've used a Molex Picoblade connector because they are rather less likely to fall off and are tiny.

 

The second FPGA block on the right shows its power and programming connections. The processor must

program the FPGA on power up (the FPGA keeps its bit pattern in RAM) and an SPI interface is used for this.

Once the FPGA is running it will use the same SPI pins to communicate with the processor but will operate as

the SPI master.

The spare signals are for as yet undefined hand shaking purposes. The FPGA to processor signals

are available on a second debugging header. (There wasn't room for CRESET_B - but it is controlled by the

processor and slow, so not very interesting. It's only used during FPGA programming.)

 

The FPGA needs 3.3V for its IO pins and 1.2V (at not much current) for its core.

 

I've used this FPGA in this configuration before - so I'm confident that it will work OK.

 

It says ICE5LP on the schematic but I think we'll actually use an ICE40UP5k, (same pin-out (or near enough)).

 

I've changed my ideas for the input and output overload monitoring as well. Now I'm using a quad comparator rather

than the micro's ADC - this ends up with fewer parts and allows the FPGA to easily deal with overload monitoring.

 

MK

  • Sign in to reply

Top Comments

  • michaelkellett
    michaelkellett over 4 years ago in reply to shabaz +1
    Hello Shabaz, thanks for your comments, It's interesting how different our instinctive approaches are to this ! I come from a background where every interrupt request (at design level ) is refused out…
  • michaelkellett
    michaelkellett over 4 years ago in reply to shabaz +1
    The FPGA will buffer the data from the ADC and write the DAC data from a pre-filled buffer (which it will loop through). On a current system (with rather more demanding data rates and a lot of the maths…
  • michaelkellett
    michaelkellett over 4 years ago in reply to shabaz

    The FPGA will buffer the data from the ADC and write the DAC data from a pre-filled buffer (which it will loop through). On a current system (with rather more demanding data rates and a lot of the maths done on the FPGA) the processor asks the FPGA for data, usually once every 4ms, this is fast enough for the highest speed of operation. The FPGA returns a data packet which will often contain no data at slow acquisition speeds, but can cope with  >10ms worth of data at the maximum data rate. So nothing goes wrong, even if the processor skips a request.

     

    To be quite clear, in the system I'm suggesting the FPGA is the SPI master, but the micro is the system and  protocol master, because it is the micro asserting ucr that makes the FPGA read a command from the micro, and the rules are that it must reply (in less than 1us) - even if it only says it has nothing to say.

     

    This code works on an STM32F407, but beware, ST keep changing the SPI and DMA so it may not work on the ARMP uP without modification.

    I seemed to have to deconf the SPI between commands - probably because I used ST's DMA_Init function - in other things I just wack the registers directly - but it is hard to read.

     

     

    static void    deconfig_spi( void )
    {
    SPI3->CR1 &= 0xffb0;                    // disable SPI by clearing 0x40 bit
    RCC_APB1PeriphResetCmd( RCC_APB1Periph_SPI3, ENABLE );
    delay_us(5);                                    // 
    RCC_APB1PeriphResetCmd( RCC_APB1Periph_SPI3, DISABLE );
    }
    
    static void    config_dma(uint16_t *tx, uint16_t *rx)
    {
        DMA_Stream_TypeDef* DMAy_Streamx;
        DMA_InitTypeDef DMA_InitStructure;
    
        // For tx, use DMA1, Channel 0, Stream 5
        // For rx, use DMA1, Channel 0, Stream 0
        // Note that FPGA is SPI master
    
        // Setup for RX:
        DMA_InitStructure.DMA_Channel            = DMA_Channel_0;
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI3->DR;
        DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)rx;        //fpga_spi_rx_buffer;
        DMA_InitStructure.DMA_BufferSize         = FPGA_RX_SPI_BUFFER_SIZE;
        DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
        DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
        DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
        DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
        DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;
        DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
        DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_HalfFull;
        DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
        DMAy_Streamx = DMA1_Stream0;
        DMA_DeInit( DMAy_Streamx );
        while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE);
        DMA1->LIFCR = 0x3f;                                                // clear all stream 0 flags
        DMA_Init( DMAy_Streamx, &DMA_InitStructure );
        DMA_Cmd(  DMAy_Streamx, ENABLE );
    
    
        // Setup for TX:
        DMA_InitStructure.DMA_Channel            = DMA_Channel_0;
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI3->DR;
        DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)tx;        //fpga_spi_tx_buffer;
        DMA_InitStructure.DMA_BufferSize         = FPGA_TX_SPI_BUFFER_SIZE;
        DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
        DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
        DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
        DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
        DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
        DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
        DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_HalfFull;
        DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
        DMAy_Streamx = DMA1_Stream5;
        DMA_DeInit( DMAy_Streamx );
        while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE);
        DMA1->HIFCR = 0xf40;                                                // clear all stream 5 flags
        DMA_Init( DMAy_Streamx, &DMA_InitStructure );
        DMA_Cmd(  DMAy_Streamx, ENABLE );
    
    }
    
    void fpga_spi_config(void)
    {
    uint32_t temp;
    PIN_SET(NV_NSS);
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
    
    GPIO_Init(GPIOC, &(GPIO_InitTypeDef) {
      .GPIO_Pin = VPIN(SPI3_MOSI) | VPIN(SPI3_MISO)| VPIN(SPI3_SCK),
      .GPIO_Speed = GPIO_Speed_50MHz,
      .GPIO_Mode = GPIO_Mode_AF,
      .GPIO_PuPd = GPIO_PuPd_NOPULL,
      .GPIO_OType = GPIO_OType_PP
      });
    
    // map GPIOC pins 10,11,12 to AF x = SPI3
    temp = GPIOC->AFR[1];
    temp &= ~0x000fff00;
    GPIOC->AFR[1] = temp | 0x00066600;
        
    config_spi();
    }
    
    static uint32_t send_fpga_command(uint16_t *tx, uint16_t *rx)
    // assumes data set up in tx buffer but will calculate checksum
    
    {
    uint16_t n_data;
    uint16_t checksum = 0;
    int i;
    uint32_t error = 0;
    
    n_data = tx[1];
    if ((n_data > 0)&&(n_data < 509))    
        {
        for(i = 0; i < (n_data + 2); i++)
            {
            checksum += tx[i];
            }
        checksum ^= 0xffff;
        tx[n_data + 2] = checksum;
        deconfig_spi();
        config_dma(tx,rx);
        config_spi();
        if (PIN_READ(FSB) == 0)                // make sure FPGA is not busy - takes about 9.5us to get here, mainly because of all this conf and deconf
            {
            PIN_SET(UCR);                                // ask it to do command
            delay_us(1);                                // always long enough for it to accept command
            if (PIN_READ(FSB) == 1)            // if it has accepted then we can
                {
                PIN_CLR(UCR);                            // clear the request
                }                                                    // the command will be read in and reply out by DMA
                                                                    // with no further uP action, FPGA will clear FSB when it's finished
            else
                {
                error = 202;                            // fpga didn't respond
                }
            }                                                        
        else
            {    
            error = 201;                                // fpga busy
            }
        }    
    else
        {
        error = 200;                                    // command number of words is too big
        }
    PIN_CLR(UCR);                                        // clean up !
    return(error);
    }

     

     

    I hope this is useful - if not just ignore it !

     

    MK

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 4 years ago in reply to michaelkellett

    Hi Michael,

     

    I see.. I forgot that you can implement any desired hardware action in the FPGA and send this together with the SPI content!

    I was considering the out-of-band separate interrupt to store a marker that the cycle was about to restart, so that when the FPGA provides the next ADC conversion, that's the zero-sine position for the multiplications to use, i.e. no timing to be done on the interrupt.

    The in-band method of sending this detail with the ADC conversion is much better for the software perspective!

     

    I'll read up on SPI DMA on the ST chip. One thing I didn't understand, is regarding the microcontroller requesting every millisecond or so. I'd been picturing the FPGA would act as a peripheral with its (say) own clock to control DAC and ADC timings, i.e. free-running, always generating the DAC output and always reading the ADC in sync, and periodically set a signal that a conversion was ready (e.g. once every millisec but would vary depending on configured clock rate), and then the microcontroller as master would read from the FPGA's register, in other words the microcontroller not needing to issue a timed request. I'm happy coding either way, I just wanted to be sure I understand the mechanism.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • michaelkellett
    michaelkellett over 4 years ago in reply to shabaz

    Hello Shabaz, thanks for your comments,

     

    It's interesting how different our instinctive approaches are to this !

     

    I come from a background where every interrupt request (at design level image) is refused out of hand and only granted if an overwhelming case can be made for it.

    The premise is that interrupts are bad and that the badness is at least proportional to n^2, probably worse. (I know that this is not entirely rational !)

    Now you know why I don't like RTOSs !

     

    So the idea in my head was that the overload comparators are connected to the FPGA which will latch any event lasting longer than maybe 100us. The FPGA will

    also be collecting ADC samples and sending out DAC samples. This activity will be synchronised to the single system clock (which runs at 16MHz). The processor will

    periodically (probably about once every ms but might be slower) ask the FPGA for a packet of data, so the FPGA can send the data packet with its associated

    overload status as one high integrity blob. So we get blobs of data with known synchronisation (between ADC and DAC) with known integrity with no interrupts.

    The processor clock is synchronised to the FPGA clock so the periodicity of the poling is completely predictable.

     

    For "SPI" I usually use two handshake signals between processor and FPGA, the FPGA being the SPI master. One handshake signal is ucr (microprocessor command ready) and the other

    if fsb (fpga spi busy). The other three signals are the usual MOSI, MISO and SCK. The FPGA is the SPI master, which means that it controls the clock and the pacing of the data.

    The processor uses DMA to transfer SPI data to memory, but doesn't need to use interrupts !

     

    The great thing is that this is an FPGA, so, to paraphrase Groucho Marx, "That's my handhsake protocol, but if you don't like it, I have others !"

     

    When the pcb layout is almost done I always try to connect all the FPGA pins to either the processor or to debug connectors.

    This stood me in good stead recently where I was able to add an additional 8 channels to an 8 channel lock in amp by connecting

    an extension board to the debug connectors. The SPI interface was hardwired between processor and primary FPGA so it had to

    multiplex the SPI between itself and the extension FPGA.

     

    So in the end I expect to have several uncommitted FPGA to processor signals.

     

    The FPGA can be re-coded by the processor without changing the processor code if we store the FPGA pattern in external flash memory.

     

    MK

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 4 years ago

    Hi Michael,

     

    This is great, another easy-to-follow explanation. Regarding the to-be-defined handshaking, one of them will need to be to a microcontroller interrupt, so it could be used for the signal for indicating when the DAC output is about to be set to zero degrees of the sine output. For the overload circuitry, maybe an OR'ing is enough into another single interrupt, i.e. the microcontroller can then try to enable the input attenuators, if it doesn't clear the issue then the microcontroller knows the source level needs adjustment.

    Perhaps the overload could also be noticed from a harmonic, but it will be good to have the hardware method too.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube