Jump to content
  • 0

FPGA audio - ADC and DAC


Ignacas

Question

Good day wizards,

I've tried to introduce myself here, but now I would like to ask for a comment on my thoughts.

My goal is to master audio processing (mainly routing and level controls for a beginning) on FPGA. 

The diagram will be very simple:

Audio signal generator => ADC => FPGA => DAC => Analyzer (Spectrum, THD, Level)

Audio signal generator will be made of two NE555 clocks with different frequencies (say 1kHz and 15kHz) to have a difference between L and R channels.

ADC will be CS5381 (24bit@48k), I2S output.

DAC will be CS4390 (24bit@48k), I2S input. (later maybe something better, but for now I'll use whatever I have in a drawer).

Once I get this AD-DA conversion running properly, I'll try routing output of the ADC to my ARTY A7 input and pass that signal directly to the DAC. At this point I would like to see a low noise, low jitter signal passing thru.

Next step could be mixing L and R signals together, adding more converters generating AES/SPDIF signals on FPGA, etc..

 

But at very beginning, I have a fundamental problem with clocks. I want to run this setup at 48kHz, so I obviously need this clock and 48k*256=12.288MHz MSCLK.

Playing around with PLL Clock wizard didn't gave me the desired result (still + or - couple MHz). I understand that it would not be a massive problem and I could run any weird frequency, but there will be a sync problem with external digital equipment if I get around to do, say AES/SPDIF interface. Finding XTAL trimmed to 12.288 is not a problem, but can I just hook it up to any desired pin and use it? I have also seen some posts (if I got it right) discouraging of using multiple clocks as it can get messy (inter-sync problems?).

Before I dive into this, I would appreciate Your insights and critics. I will post all my story here as soon as I have something to share with You:)

Thank You!

 

 

Link to comment
Share on other sites

Recommended Posts

BTW, one more thought: If all you need to generate is a 1 kHz tone at 96 ksps, a perfect wavetable needs only 96 entries for a full period (or using simple symmetry, 1/4 of that). I don't see where the 12 bit come in. If in doubt, it should be trivial to run the wave calculation in floating point on a Microblaze processor, at least for debugging.

Now the THD+N reading from your screenshot is -74 dBc, which is suspiciously close to the quantization noise of a Nyquist-rate 12-bit DAC (eq 12, 6*12+1.7). A modern converter should reach -100 dBc, which would be your 0.001 % target.

Link to comment
Share on other sites

On 4/20/2018 at 10:23 AM, Ignacas said:

Moving forward.. 4096 (12 bit) LUT with 32bit sine values played via 32bit DAC at 48kHz gives this. Not perfect but I'm getting there..

Hi,

if you like, try my code based on splines. Historically, it's written for 18x18 multipliers but nowadays Artix offers 25x18 at the same cost so it can still be optimized.

I don't have any THD numbers at hand but the fixed-point spline math is about 16 bit accurate to the double precision template. If needed, simply use more segments (see the included Octave .m script, right now 16 for a full cycle).

Link: fix the path to iverilog.exe in the .bat file; run and load the "loadThisIntoGtkWave..." file into gtkwave => one cycle sine wave.

https://drive.google.com/file/d/1xChgXBTQU8rRKrukCGVDTaeEgbNpXQeX/view?usp=sharing

Here is the main function, tested at 150 MHz (1 output sample / clock cycle). Note, this needs the "wavetable" data file from the archive above to be functional.

If I clean out the bus interface and the optional (commented) "aux" tag path, it's actually quite short.

// # spline-based cyclic waveform generator #
// # (C) 2015-2018 Markus Nentwig           #
// # This code is in the public domain      #
module spline(input wire i_clk, input wire [17:0] i_x, output wire [17:0] o_y, 
//	      input wire [7:0] 	i_aux, output wire [7:0] o_aux,
	      input wire [31:0] Si_addr,
	      input wire [31:0] Si_data,
	      input wire 	Si_we);
   parameter ADDRMASK = 32'h0000003F;
   parameter ADDRVAL = 32'h20000000;
   (* ram_style = "distributed" *) reg [17:0] 			mem[0:63];   

   initial begin
`include "wavetable_WAVEID3.v"
   end
   
   // === coefficient write ===
   localparam NWRDELAY = 2;
   (* DONT_TOUCH = "TRUE" *)reg [5:0] 			wa [1:NWRDELAY];
   (* DONT_TOUCH = "TRUE" *)reg [17:0] 			wd [1:NWRDELAY];
   (* DONT_TOUCH = "TRUE" *)reg 			we [1:NWRDELAY];
   
   genvar 			j;
   generate
      for (j = 2; j <= NWRDELAY; j = j + 1) begin
	 always @(posedge i_clk) begin
	    wa[j] <= wa[j-1];
	    wd[j] <= wd[j-1];
	    we[j] <= we[j-1];
	 end
      end
   endgenerate
   
   always @(posedge i_clk) begin
      wa[1] <= Si_addr;
      wd[1] <= Si_data;      
      we[1] <= Si_we & ((Si_addr & ~ADDRMASK) == ADDRVAL);
      
      if (we[NWRDELAY])
	mem[wa[NWRDELAY]] = wd[NWRDELAY];
   end      
   
   // MSB unsigned to index coeff bank
   wire [3:0] 		xMSB0 = i_x[17:14];
   
   // LSB half-range shift down as signed variable in polynomial
   wire signed [17:0] 	xLSB0 = {{5{!i_x[13]}}, i_x[12:0]};
   
   // === pipeline variables ===
   // Note: this is the shortest possible pipeline length that utilizes all hardware registers
   // (PREG and MREG) in the inferred DSP48 blocks (Artix 7)
   localparam PMAX = 14;
   localparam Nc0 = 13;   
   localparam Nc1 = 9;   
   localparam Nc2 = 5;   
   localparam Nc3 = 1;   
   reg signed [17:0] 	xLSB[1:PMAX];
   reg signed [17:0] 	c3[1:Nc3];
   reg signed [17:0] 	c2[1:Nc2];
   reg signed [17:0] 	c1[1:Nc1];
   reg signed [17:0] 	c0[1:Nc0];
   reg signed [17:0] 	acc[1:PMAX];
//   reg signed [7:0] 	aux[1:PMAX];
   
   // === clear aux ===
//   generate
//      for (j = 1; j <= PMAX; j = j + 1) begin
//	 initial aux[j] = 8'd0;
//      end
//   endgenerate
   
   assign o_y = {!acc[PMAX][17], acc[PMAX][16:0]};   
//   assign o_aux = aux[PMAX];
   
`ifdef SIM
   wire [17:0] 		DEBUG_acc3 = acc[3];   
   wire [17:0] 		DEBUG_acc7 = acc[7];   
   wire [17:0] 		DEBUG_acc11 = acc[11];   
   reg [31:0] 		PMAX_NOM;   
`endif
   
   // === delay variables through pipeline ===
   genvar 		i;
   generate
      for (i = 2; i <= PMAX; i = i + 1) begin
	 always @(posedge i_clk) begin
	    xLSB[i] 	<= xLSB[i-1];	 
	    if (i <= Nc3)
	      c3[i] 	<= c3[i-1];	    
	    if (i <= Nc2)
	      c2[i] 	<= c2[i-1];	    
	    if (i <= Nc1)
	      c1[i] 	<= c1[i-1];	    
	    if (i <= Nc0)
	      c0[i] 	<= c0[i-1];	    
	    //	    aux[i] 	<= aux[i-1];	    
	 end
      end
   endgenerate
   
   integer NEXT; integer CURR;
   always @(posedge i_clk) begin      

      // === input to pipeline ===
      c3[1] 	<= mem[(xMSB0<<2)+0];
      c2[1] 	<= mem[(xMSB0<<2)+1];
      c1[1] 	<= mem[(xMSB0<<2)+2];
      c0[1] 	<= mem[(xMSB0<<2)+3];      
      xLSB[1] 	<= xLSB0;
//      aux[1] 	<= i_aux;      
      
      // === calculation of pipeline stages ===      
      // x has 13 fractional bits (input: 17; 4 MSBs stripped off)
      // - after multiplication with x [1, 2, 3, 4], arithmetic right-shift
      //   by 13 bits keeps the decimal point in place. This is equivalent
      //   to floor(...).
      // - for correct midpoint rounding, add 0.5 (1 << 12)
      //   now equivalent to floor(x+0.5)
      NEXT = 2; CURR = 1;
      acc[NEXT] <= c3[CURR];
      
      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR]; // coefficient ram

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR]; // DSP48 input reg

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= (acc[CURR] * xLSB[CURR] + $signed(1 << 12)) >>> 13;
      
      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR] + c2[CURR];
      
      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR]; // output reg

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR]; // input reg

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= ((acc[CURR] * xLSB[CURR] + $signed(1 << 12)) >>> 13);

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR] + c1[CURR];

      NEXT = NEXT + 1; CURR = CURR + 1;
      acc[NEXT] <= acc[CURR]; // output reg

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR]; // input reg

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= ((acc[CURR] * xLSB[CURR] + $signed(1 << 12)) >>> 13);

      NEXT = NEXT + 1; CURR = CURR + 1;       
      acc[NEXT] <= acc[CURR] + c0[CURR];

`ifdef SIM
      PMAX_NOM <= NEXT;
`endif
   end
endmodule   

 

Link to comment
Share on other sites

Moving forward.. 4096 (12 bit) LUT with 32bit sine values played via 32bit DAC at 48kHz gives this. Not perfect but I'm getting there..

I've used this boad for testing https://www.ebay.com/itm/ES9028Q2M-ES9028-I2S-input-decoder-board-mill-board-DAC-balanced-output/132317494575?hash=item1ecebcbd2f:g:4VcAAOSwOuRZrMaq

The goal is to get THD+N below 0,001%. It seems I might need to learn FIR filtering for that.

image.png.212ced129bde7c50d4cfac04bbb5ca8b.png

Link to comment
Share on other sites

Good day everyone, just a quick update.

Everything is going quite slow but fine with this project. Unfortunately I can only spare up to 1-2 hours a day for it, but have learned a lot already.

So far I've managed to:

Design the DAC pcb and generate all clocks for it (16 bit, 48kHz);

Build quite stable square, triangle and sine (1000 sample LUT) generators;

Make small internal audio switcher (dependant on hardware switch position);

Achieve ~0.2% THD+N on the differential (DRV134) output with 1kHz @ ~4Vpp;

Next step is to wait for revisioned and better quality PCBs and reclock it for 24bits. Then it will be the time either for AES/SPDIF output or ADC design. AES output would be nice to have for development as I have a RTW AES audio meter (img attached) collecting dust.

I've tried to include MicroBlaze with TCP server stuff so I could control the audio switch and generators, but it just got messy and I still couldn't find a way how could I make MicroBlaze to interact with my RTLs.. This will have to wait until ADC is finished.

The saga continues..

DAC.jpg

28944182_10204382683843303_1243229010_o.pngmaxresdefault[1].jpg

Link to comment
Share on other sites

If clocking is your problem, with a MMCME2_BASE with a 100MHz clock you can get to:

100,000,000Hz * 7.25 / 59.0 = 12,288,135 Hz

Out by about 11 parts per million - The 100MHz oscillator on the Arty A7 (ASEM1-1 0 0.0 0 0MHZ-LC-T) is +/-50 ppm, so maybe nobody will really notice. :)

 

 

Link to comment
Share on other sites

@hamster that is impressive list of projects You have done. Also few related to audio - Its going to be quite a nice read for me about spdif and Analog Wing.

STGL5000 example is nice, however I doesn't seem to face and solve same (clocking) problems as I have.. Thanks anyway!

Link to comment
Share on other sites

Check your warnings and understand every one of them (e.g. look out for logic that gets optimized away. Often it indicates a missing connection). You must fix "critical warnings", if there are any. Otherwise timing may be completely off (and if timing closure fails, the implementation is NOT "best effort" in any useful sense).

If you're certain the logic is correct, you may have a hazard problem: Logic levels are guaranteed to be stable at clock edges within setup / hold times (see static timing analysis), but in-between they may do whatever they like. And when an output signal acts as a clock for an external chip, hazards may cause double-triggering of the edge-sensitive input.

If in doubt, put e.g. (* DONT_TOUCH = "TRUE" *) reg xyz into every signal that leaves the FPGA to prevent any combinational logic downstream of the final register that could cause glitches.

Link to comment
Share on other sites

Well, i meant OOP in away that You have a definition of the object and then have to instantiate it (even several times if need be). The GUI way is quite ok, once You figure out that it still has to be generated and set as top, later its much easier to understand what is going on with the desing. Yesterday I've figured that I can use external editor (Sublime) and that was great performance enhancement for me.

It took some time to realise that for this purpose I need PISO shifter and after whole day today I've got clocks and that shifter working.

image.thumb.png.9d7b1e0cbbfa42ba95151f713dbb41ec.png

sys_clock = 100MHZ, mclock = 12.288MHz, bclock = 1.536MHz, lrclock = 48kHz;

out_sample_load is a pulse to sample the data,

sdata is serial i2s data.

This looks ok form me, however my dac (CS4390) does not give me any signal. Im not sure what is wrong here. Any thoughts?

Link to comment
Share on other sites

1 hour ago, Ignacas said:

verilog is OOP and all blocks has to be instantiated.

@Ignacas,

No, Verilog is not OOP. In the context of this thread VHDL and Verilog are languages that along with the appropriate synthesis and place and route tools allow you to express digital logic in a particular target architecture. You can certainly use a GUI like the board design schematic approach offered by Vivado to connect blocks of IP and user RTL code and have some success. In the beginning at least this might seem to be quicker and easier. If the overall objective is to use FPGAs to experiment with audio data I think that you'll find this flow will be in the way of getting projects done.

At this point in the discussion I'd like to say that in the long run you're likely to be better off learning an HDL and using a text editor to create your own IP. That's just an observation that may well be immaterial to your immediate objectives. Regardless, I'll follow the thread because I'm interested in seeing how this works out. Whether you use schematic symbols or text source files to do it FPGA development is computer aided digital design. You may well prove me wrong but I believe that you'll get to your destination faster developing a competency in VHDL or Verilog. Of course I might be wrong about where I think your destination is.

For what, if anything, it worth.

Link to comment
Share on other sites

@xc6lx45 thanks for picturesque code snippet. That actually opened my eyes - verilog is OOP and all blocks has to be instantiated.

So I've managed to hack this together:

image.thumb.png.adbd2b3fdd7a55dfd944f70655aeaa77.png

and even write test bench with 100MHz sysclock, so I could simulate the whole thing.

Most of the thing seems to be working, except the output serializer.. Just cant make it work..

I was looking at @bikerglen i2s example for couple hours, but I just cant figure it out, even if I throw everything input related out. Somehow I'm not completely sure how it works.

Any suggestions where to look/read or maybe the whole concept is wrong and only possible options is @bikerglen I/O transceiver option?

 

 

Link to comment
Share on other sites

Welcome to FPGA hell... I think it's the same for everyone, three days doesn't seem that much.
And yes, this isn't Visual Studio... Emacs Verilog mode (e.g. indent-region) is probably the next best thing...

>>Also what is the meaning of the TOP source? For me all the blocks are the same, what should be in the top then?

You need to make one module the "top" level. Vivado will instantiate that module once and consider its ports top-level connections. Those are then the same names as in the constraints, to assign a certain package pin and IOSTANDARD.

I'm not familiar with graphical entry but for plain .v files added as sources, go to Vivado, "Project manager", "Sources", click "Hierarchy" at the bottom, then right-click your top level design in the list and select "set as Top" from the drop-down menu.

For example, this is how a "top" module may look (I think you can also use any other name than "top" as long as you "set it as top" from the menu).

`default_nettype none
`include "xyz.v"
module top(input wire CLK12, 
	   output wire [1:0]  LED, 
	   output wire 	      RGB0_Red, 
	   output wire 	      RGB0_Green, 
	   output wire 	      RGB0_Blue, 
	   input wire [14:1]  pioA, // 14 
	   input wire [23:17] pioB, // 7
	   input wire [48:26] pioC, // 23
	   inout wire [7:0]   ja, // 8
	   input wire [1:0]   BTN, 
	   input wire [1:0]   xa_n, 
	   input wire [1:0]   xa_p);

   // 31:16: breaking change
   // 15:0 non-breaking change
   localparam REVISION = 32'h00020010;   
			 
   //          _               _                       
   //         | |             | |                      
   //     ___ | |  ___    ___ | | __ __ _   ___  _ __  
   //    / __|| | / _ \  / __|| |/ // _` | / _ \| '_ \ 
   //   | (__ | || (_) || (__ |   <| (_| ||  __/| | | |
   //    \___||_| \___/  \___||_|\_\\__, | \___||_| |_|
   //                                __/ |             
   //                               |___/             
   // note: 12 MHz would be too low when running at 30 MHz FTDI clock
   wire 		      CLK; 
   wire 		      CLKDAC;
   wire 		      CLK300;   
   clk12to300 myPll1(.clk_in1(CLK12), .clk_out1(CLK300), .locked());
   clkMul myPll2(.clk_in1(CLK300), .clk_out1(CLK), .clk_out2(CLKDAC));
   
...

The two clocking blocks are then added as IP with the names of clk12to300 and clkMul.

PS: Disable generation of the "reset" input. You don't need it in a simple design, and it's one more thing that can go wrong.

Link to comment
Share on other sites

I still dont get the basic parts of design..

if I have something like this

image.png.f19505c6d56e568499f745cbe69be4e0.png

It seems that RTL is still looking for hardware ports by the name of its output - it ignores if I have those nice ports created..

why is it so? or am I missing something? It this case I could just delete these ports and it wouldn't care.

Same with the clock.. If i start clocking wizard, setup desired freq and then rightclick that block and use this wiring wizard it connects those two ports (reset and sys_clock). But how does it know where to get the clock as my constraints file is commented out..

I would think that if I declare

set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { CLK100MHZ }]; #IO_L12P_T1_MRCC_35 Sch=gclk[100]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports { CLK100MHZ }];

then add port named CLK100MHZ and connect it to clocking wizard input would be the right pattern, but that doesnt seem to be the case.

Also what is the meaning of the TOP source? For me all the blocks are the same, what should be in the top then?

Learning process is somewhat awkward as I am looking at the same thing for the third evening (pin and clock errors).. Vivado is obviously powerful tool but lacks intelligence and support in the very begining starting with text formating and intelli-sense type of suggestions. As a Visual Studio and PHPStorm user I am a bit surprised not get these tools.

However, You guys seem to be all right:) Thanks for Your help and support!

Link to comment
Share on other sites

If I take it to extremes, the answer is surprisingly complex (this is how it's done inside the clock management tiles or a cellphone, for example).

For household use, take (e.g.) a 32 bit counter and add "delta" at a high frequency, e.g. 100 MHz. Don't check for overflow, it will wrap around cyclically (which is the "correct" way to behave in this application. E.g. 0xFFFFFFFF + 3 becomes 0x00000002)

For example, at 100 MHz clock, a delta of 1 gives a cycle time of 42.9 seconds (2^32 / 100e6).
A delta of 43 gives a cycle time of one second. A delta of 42950 gives a cycle time of exactly 1 ms => 1 kHz.

Now we've got a 32 bit number. Take the highest bits (as many as the DAC needs), voilà, a sawtooth generator.

Plug that into your  wavetable (you'll have to recalculate for a 0..31 range if using five ramp bits).

Simple lookup from a block RAM (aka "nearest-neighbor / zero-order interpolation") will give abysmal audio quality, or the wavetable grows so large that it doesn't fit into the FPGA. So I need higher-order interpolation or a different algorithm (e.g. CORDIC for sine), and things get messy.

PS: When working with signed numbers, check Verilog's "signed" keyword. It's not mandatory, but makes life easier.

Link to comment
Share on other sites

I'm trying to construct different signal generators.. so I could play some data to my breadboarded dac.

The idea is to have a tick generator at, say 1kHz frequency and then different sort of samplers to play samples.

https://gist.github.com/ignazas/dc15fb4afd7e0204825baa359cd17bbb

This is what i have hacked together to get sinus and I also thoughts regarding sawtooth - this one could be generated as a simple counter but i dont know how to control the frequency..

Even if I have a predefined step(resolution), say (full scale)/256, i will get signal frequency 256 times slower than tick.. Any ideas how to sort that out? Do I absolutely need some sort of freq multiplier in same RTL to play samples faster?

Link to comment
Share on other sites

If you copied it verbatim, the names don't match:

set_property ... lrck but "Problem ports": lrclk
mlck vs mclk

etc.

The idea is that every pin going out of your top level design is assigned a package pin and IOSTANDARD. Otherwise, VIVADO will randomly assign package pins, but it does not pick an IO standard and that ultimately leads to the error message.

 

 

Link to comment
Share on other sites

thanks for the comment, ive tried that now..

##Pmod Header JA

set_property -dict { PACKAGE_PIN G13 } [get_ports { sdata }];
set_property -dict { IOSTANDARD LVCMOS33 } [get_ports { sdata }]; #IO_0_15 Sch=ja[1]
set_property -dict { PACKAGE_PIN B11 } [get_ports { sclock }];
set_property -dict { IOSTANDARD LVCMOS33 } [get_ports { sclock }]; #IO_L4P_T0_15 Sch=ja[2]
set_property -dict { PACKAGE_PIN A11 } [get_ports { mlck }];
set_property -dict { IOSTANDARD LVCMOS33 } [get_ports { mlck }]; #IO_L4N_T0_15 Sch=ja[3]
set_property -dict { PACKAGE_PIN D12 } [get_ports { lrck }];
set_property -dict { IOSTANDARD LVCMOS33 } [get_ports { lrck }]; #IO_L6P_T0_15 Sch=ja[4]

but still getting the same :

[DRC NSTD-1] Unspecified I/O Standard: 136 out of 136 logical ports use I/O standard (IOSTANDARD) value 'DEFAULT', instead of a user assigned specific value. This may cause I/O contention or incompatibility with the board power or connectivity affecting performance, signal integrity or in extreme cases cause damage to the device or the components to which it is connected. To correct this violation, specify all I/O standards. This design will fail to generate a bitstream unless all logical ports have a user specified I/O standard value defined. To allow bitstream creation with unspecified I/O standard values (not recommended), use this command: set_property SEVERITY {Warning} [get_drc_checks NSTD-1].  NOTE: When using the Vivado Runs infrastructure (e.g. launch_runs Tcl command), add this command to a .tcl file and add that file as a pre-hook for write_bitstream step for the implementation run.

Problem ports: pdin_l[31:0], pdin_r[31:0], pdout_l[31:0], pdout_r[31:0], bclk, lrclk, mclk, pdin_req, pdout_ack, rst, sdin, and sdout.

Link to comment
Share on other sites

Hi,

split the set_property into two lines per pin: one for PACKAGE_PIN G13, one for IOSTANDARD LVCMOS33 (99 % certain I've tried myself to combine them and it didn't work).

Audio processing on FPGA is an uphill battle, but I can learn a lot from that one.

If I just write out the signal flow, e.g. channel and master faders on a mixing console, I'll run out of multipliers really quickly.

But since the sample rate is extremely low, I can use a dual port RAM-multiplier, state machine and mux a whole mixing console onto a single DSP block, channel filters and all. Taken to extremes, it's like programming in assembly language, except you're designing the language, the compiler and the computer at the same time besides the DSP algorithms :). Even a small FPGA can do a crazy amount of processing, but it's much more challenging than e.g. using a CPU.

I'd use a CODEC, not separate AD and DA. And I'd avoid "slipping" sample rates (e.g. between one crystal on the board and another on the PC end) at all costs, or at least do it in software on the PC. Fixed-ratio resampling (e.g. 44.1 <-> 48 kHz) is painful enough, variable-rate with e.g. a control loop around a Farrow stage is a very serious project in its own respect,

Link to comment
Share on other sites

Good day everyone,

so I'm trying out @bikerglen i2s IP, but facing some general Vivado error even before that..

My current setup (Arty-7):

 

image.png.6d26d57041585665617097a9646a381d.png

But when generating bitstream im getting:

image.thumb.png.143a1a72f8dce9b5dd64caaaa14d14be.png

I was trying to google that one, but unfortunately its not clear why Vivado does not recognize my default declarations in .xdc file.. any ideas?

Link to comment
Share on other sites

First thing to do is understand the data sheets for your audio DAC and ADC. The SCLK period for the CS5381 is 72-145 ns depending on the mode. The SCK frequency will define the data rate, the LRCK will define the digital sample rate including two channels of data. Actual delays will include the ADC and digital filters in the CS5381.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...