enjoy-digital / litex

Build your hardware, easily!
Other
2.89k stars 555 forks source link

Feature Request: Add FPGA temperature sensors #1257

Open troibe opened 2 years ago

troibe commented 2 years ago

Some FPGAs feature internal temperature sensors. It would be great to integrate these in LiteX and access them in Linux to read out the current operating temperature of the chip.

enjoy-digital commented 2 years ago

Here it is: https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/xadc.py :) You can search for XADC on LiteX-Boards to see an example of integration. On my side I've only used it with custom software, but I think @antmicro developed a Linux driver for it.

tcal-x commented 2 years ago

On a somewhat related note, I had played around with the XADC for audio input using a Xilinx reference design. I wish I had the time to extract it as a core for LiteX -- it would make a nice match for the sigma-delta DAC :)

The Xilinx tools wrap the XADC in some generator IP, but I assume the underlying primitive can be directly instantiated. I'm not sure of the state of support from open source tools.

JamesTimothyMeech commented 1 year ago

Does anyone have a bare minimum example C program the samples the XADC on a LiteX generated SoC?

enjoy-digital commented 1 year ago

You can find some code here over LitePCIe: https://github.com/enjoy-digital/litepcie/blob/master/litepcie/software/user/litepcie_util.c#L67-L76

JamesTimothyMeech commented 1 year ago

Thanks!

JamesTimothyMeech commented 1 year ago

Does anyone have any clues on how to use LiteX to generate verilog which allows me to read from the differential of VAUXP[14] and VAUXN[14] on the XADC? I have VAUXN[14] grounded in my implementation so the differential of VAUXP[14] and anything grounded would also work.

I can only see CSRs for TEMPERATURE, VCCINT, VCCAUX, and VCCBRAM from this diagram https://docs.xilinx.com/r/en-US/ug480_7Series_XADC/XADC-Overview in my generated csr.h file. Can I get at VAUXP[14] and VAUXN[14] by just incrementing the CSR address I read from to the correct value or do I need different verilog than what LiteX is generating? If this functionality is not yet implemented in LiteX I happy to try and build it myself if we can converge to a plan on how to implement it!

JamesTimothyMeech commented 1 year ago

I've been fighting with Vivado for a week and haven't even been able to get a simple ADC example up and running! My plan was to use what I learnt from the Vivado example to get a design working in LiteX. Any tips about how to do this on LiteX could save me a lot of time! Do I need to do anything on the HDL side or do I just need to find the right address to run XilinxSystemMonitorChannel with?

JamesTimothyMeech commented 1 year ago

According to this: https://docs.xilinx.com/r/en-US/ug480_7Series_XADC/XADC-Register-Interface it looks like I need to do:

XilinxSystemMonitorChannel(name="vaux14",      addr=0x1E, bits=12, desc=[
        "Raw VAUX14 value from XADC.",
        "The external voltage I want to measure",
    ]),

I'll try it and post here if it works

enjoy-digital commented 1 year ago

Hi @JamesTimothyMeech,

we've only implemented what was useful for us, but the structure should be in place to read the other XADC channels. I think you are in the right direction in your last message.

JamesTimothyMeech commented 1 year ago

Thanks for the pointer that I am heading in the right direction. I tried what I mentioned in the previous message and I can see the CSR is successfully generated and I am able to use it to write a C program which compiles and runs:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <generated/csr.h>

void 
print_double(double x) {
    int i = (int)floor(x);
    double remainder = x - (double) i;
    remainder = remainder * 1000000;
    printf("%i",i);
    printf(".");
    printf("%i",(int) remainder);
}

int main(int argc, char** argv) {
   printf("hello, world\n");
   volatile unsigned int *temperature = (volatile unsigned int *) CSR_XADC_TEMPERATURE_ADDR;
   double temperature_value = *temperature * (503.975/4096) -273.15;
   printf("FPGA Temperature = ");
   print_double(temperature_value);
   printf(" °C\n");

   volatile unsigned int *vccint = (volatile unsigned int *) CSR_XADC_VCCINT_ADDR;
   double vccint_value = (double) *vccint / 4096 * 3;
   printf("VCCINT = ");
   print_double(vccint_value);
   printf(" V\n");

   volatile unsigned int *vccaux = (volatile unsigned int *) CSR_XADC_VCCAUX_ADDR;
   double vccaux_value = (double) *vccaux / 4096.0 * 3.0;
   printf("VCCAUX = ");
   print_double(vccaux_value);
   printf(" V\n");

   volatile unsigned int *vccbram = (volatile unsigned int *) CSR_XADC_VCCBRAM_ADDR;
   double vccbram_value = (double) *vccbram / 4096.0 * 3;
   printf("VCCBRAM = ");
   print_double(vccbram_value);
   printf(" V\n");

   volatile unsigned int *vaux14 = (volatile unsigned int *) CSR_XADC_VAUX14_ADDR;
   double vaux14_value = (double) *vaux14 / 4096.0 ;
   printf("Random Sample = ");
   print_double(vaux14_value);
   printf(" V\n");

   return 0;
}

Unfortunately I get zero for the analog voltage which I am trying to measure:

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 39.865722 °C
VCCINT = 1.4150 V
VCCAUX = 1.792968 V
VCCBRAM = 1.4150 V
Random Sample = 0.0 V

Is there anything obviously wrong with the setup I have posted here? I have wired my circuit with a voltage divider for approximately 0.3V and measured the voltage on the A10 pin to check that it is really there. I have wired my 0.3V signal to the A10 pin as the schematic says that this is connected to AD14_P which is the ADC channel I have selected in my program and verilog. I have grounded A11 as the schematic says that this is AD14_N: https://digilent.com/reference/_media/programmable-logic/arty-a7/arty-a7-e2-sch.pdf. I have attached a photo of my circuit: Image

JamesTimothyMeech commented 1 year ago

This is a slightly better photo of my wiring: Image (1)

JamesTimothyMeech commented 1 year ago

What is this line in the xadc.py file doing?

analog_layout = [("vauxp", 16), ("vauxn", 16), ("vp", 1), ("vn", 1)]
JamesTimothyMeech commented 1 year ago

I'm able to get a voltage reading using

XilinxSystemMonitorChannel(name="vaux0",      addr=0x10, bits=12, desc=[
        "Raw VAUX0 value from XADC.",
        "The external voltage I want to measure",
    ]),

on my hardware but as I change the voltage connected to the A5 pin on my board (the pin header connection which the documentation claims that channel 0 is connected to: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual) the voltage is constant and unchanged:

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 47.371209 °C
VCCINT = 1.4150 V
VCCAUX = 1.792968 V
VCCBRAM = 1.4882 V
Random Sample = 0.46630 V
JamesTimothyMeech commented 1 year ago

I observed a similar problem when using Vivado and Vitis to do the same thing and I was able to fix it by adding a pin constraints file for the Arty board and uncommenting the lines for VAUXN0 and VAUXP0. Is there any similar thing I can do in LiteX?

JamesTimothyMeech commented 1 year ago

I tried adding these lines to the digilent_arty.py file in litex-boards but I was unsure how to include them in the add_platform_command function so that they do not throw errors but still have their desired effect:

set_property -dict { PACKAGE_PIN C14   IOSTANDARD LVCMOS33 } [get_ports { vaux0_n  }]; #IO_L1N_T0_AD0N_15     Sch=ck_an_n[5] ChipKit pin=A5
set_property -dict { PACKAGE_PIN D14   IOSTANDARD LVCMOS33 } [get_ports { vaux0_p  }]; #IO_L1P_T0_AD0P_15     Sch=ck_an_p[5] ChipKit pin=A5
JamesTimothyMeech commented 11 months ago

My current design doesn't seem to generate any IO ports for the XADC. Is there any way I can force LiteX to do this? There are 8 IO ports but none are for the XADC Screenshot from 2023-10-06 18-22-32

JamesTimothyMeech commented 8 months ago

I recently made some progress on this with some help and advice from @bunnie. Now I need to figure out what to do with the analog.vbus_div, analog.usbdet_p, analog.usbdet_n, analog.vbus_div_n, analog.usbdet_p_n, and analog.usbdet_n_n signals here as they do not exist for the arty:

self.comb += analog_pads.vauxp.eq(Cat(dummy4,          # 0,1,2,3
                                             analog.noise1,        # 4
                                             dummy1,               # 5
                                             analog.vbus_div,      # 6
                                             dummy5,               # 7,8,9,10,11
                                             analog.noise0,        # 12
                                             dummy1,               # 13
                                             analog.usbdet_p,      # 14
                                             analog.usbdet_n,      # 15
                                        )),
            self.comb += analog_pads.vauxn.eq(Cat(dummy4,          # 0,1,2,3
                                             analog.noise1_n,      # 4
                                             dummy1,               # 5
                                             analog.vbus_div_n,    # 6
                                             dummy5,               # 7,8,9,10,11
                                             analog.noise0_n,      # 12
                                             dummy1,               # 13
                                             analog.usbdet_p_n,    # 14
                                             analog.usbdet_n_n,    # 15
                                        )),

https://github.com/betrusted-io/betrusted-soc/blob/6f8a181021be82ef0145f123c1650da3650edf07/betrusted_soc.py#L1404-L1482

JamesTimothyMeech commented 8 months ago

I can't just connect them all to dummy signals as this causes synthesis errors in Vivado:

        self.comb += analog_pads.vauxp.eq(Cat(dummy4,          # 0,1,2,3
                                             analog.ana_vp,    # 4
                                             dummy1,           # 5
                                             dummy1,           # 6
                                             dummy5,           # 7,8,9,10,11
                                             dummy1,           # 12
                                             dummy1,           # 13
                                             dummy1,           # 14
                                             dummy1,           # 15
                                        )),
        self.comb += analog_pads.vauxn.eq(Cat(dummy4,          # 0,1,2,3
                                             analog.ana_vn,    # 4
                                             dummy1,           # 5
                                             dummy1,           # 6
                                             dummy5,           # 7,8,9,10,11
                                             dummy1,           # 12
                                             dummy1,           # 13
                                             dummy1,           # 14
                                             dummy1,           # 15
                                        )),

Vivado errors:

---------------------------------------------------------------------------------
Finished Final Netlist Cleanup
---------------------------------------------------------------------------------
ERROR: [Synth 8-5535] port <analog_ana_vn> has illegal connections. It is illegal to have a port connected to an input buffer and other components. The following are the port connections :
Input Buffer:
    Port VN of instance XADC(XADC) in module <digilent_arty>
Other Components:
    Port VN of instance XADC(XADC) in module digilent_arty
    Port VAUXN[4] of instance XADC(XADC) in module digilent_arty

ERROR: [Synth 8-2918] Failing due to illegal port connections
ERROR: synthesis optimization failed, fatal insert_io failure.
bunnie commented 8 months ago

The error doesn't seem to be on a dummy signal -- it's complaining about VAUXN[4], which would be what is currently connected to analog.ana_vn.

The problem is more likely that analog.ana_vn doesn't map to the right pin. If you look at the Xilinx pin map. What pin is this mapped to, on what package?

bunnie commented 8 months ago

This looks like it might be the pin map for the Arty A7:

https://github.com/Digilent/digilent-xdc/blob/fa60017608b914b6765c8620e85a3b97a36179bf/Arty-A7-100-Master.xdc#L124-L135

You'll want to pick a pair of differential signals, for example:

#set_property -dict { PACKAGE_PIN C14   IOSTANDARD LVCMOS33 } [get_ports { vaux0_n  }]; #IO_L1N_T0_AD0N_15     Sch=ck_an_n[5] ChipKit pin=A5
#set_property -dict { PACKAGE_PIN D14   IOSTANDARD LVCMOS33 } [get_ports { vaux0_p  }]; #IO_L1P_T0_AD0P_15     Sch=ck_an_p[5] ChipKit pin=A5

Pins C14/D14 would be channel 0 +/- signals. If you picked that, you'd have to put the analog signal as the first in the Cat() list so it's mapped to analog 0, and then you'd need to make sure that on the Litex side you're mapped to the pins that correspond to that, i.e. these pins:

https://github.com/litex-hub/litex-boards/blob/52aeec00d78299fdb9270e24486035e997d93f68/litex_boards/platforms/digilent_arty.py#L228-L229

JamesTimothyMeech commented 8 months ago

Ah ok thanks that explains why I got past the Vivado errors when I changed my code to this:

        self.comb += analog_pads.vauxp.eq(Cat(dummy4,          # 0,1,2,3
                                             analog.vaux4_p,    # 4
                                             dummy1,           # 5
                                             dummy1,           # 6
                                             dummy5,           # 7,8,9,10,11
                                             dummy1,           # 12
                                             dummy1,           # 13
                                             dummy1,           # 14
                                             dummy1,           # 15
                                        )),
        self.comb += analog_pads.vauxn.eq(Cat(dummy4,          # 0,1,2,3
                                             analog.vaux4_n,    # 4
                                             dummy1,           # 5
                                             dummy1,           # 6
                                             dummy5,           # 7,8,9,10,11
                                             dummy1,           # 12
                                             dummy1,           # 13
                                             dummy1,           # 14
                                             dummy1,           # 15
                                        )),

I have this for the pin connections:

Subsignal("vaux4_n", Pins("C5"), IOStandard("LVCMOS33")),    # DVT
Subsignal("vaux4_p", Pins("C6"), IOStandard("LVCMOS33")),      # DVT

I'm just trying to open the .dcp file but my Vivado keeps crashing >.< I'll try on my Windows PC before I try to measure anything else using the hardware. digilent_arty_route.zip

JamesTimothyMeech commented 8 months ago

I still only see 8 IO ports in the .dcp file so my code must still be missing something despite me adding the pads to self.comb and the design running through Vivado without errors. Screenshot from 2024-01-07 01-35-19

JamesTimothyMeech commented 8 months ago

Ah there are 24 pages of schematics and I only checked one of them. The pins do look connected now! I will try to measure something on the hardware again tomorrow! Screenshot from 2024-01-07 01-56-29

JamesTimothyMeech commented 8 months ago

Despite successfully getting something that appears to be properly connected in Vivado I am unable to get voltage values other than zero printing out in my C program. In the attached screenshot in Vivado I see the MUXADDR on the XADC is unconnected which could explain why I cannot measure the signals on VAUX4. It does not explain why I cannot measure the signals on VP / VN as they do not go through the mux. I measured the voltages in my setup with a volt meter so I am sure they are there. Are there any obvious mistakes in my breadboard? Image A second better angle of the breadboard: Image

JamesTimothyMeech commented 8 months ago

Here is an image in Vivado showing that MUXADDR is not connected and many of the XADC signals are grounded as expected. Should I be worried about the IBUF instances on the analog lines? Screenshot from 2024-01-07 17-21-32

JamesTimothyMeech commented 8 months ago

I attach my xadc.py, digilent_arty.py board and target files, csr.h file and adc_test.c file in a zip file in case I made any obvious mistakes. I would happily make pull requests to integrate this with the public version of LiteX if we can get this working! This is the output of my adc_test.c program that shows the internal XADC channels working but the two external ones not working:

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 43.187823 °C
VCCINT = 1.4882 V
VCCAUX = 1.792236 V
VCCBRAM = 1.4882 V
Random Sample = 0.0 V
Random Sample = 0.0 V

Files.zip

bunnie commented 8 months ago

image

this is what mine looks like. IBUF is what I'm using, and if you click on it you can confirm what pin it's mapped to.

bunnie commented 8 months ago

I attach my xadc.py, digilent_arty.py board and target files, csr.h file and adc_test.c file in a zip file in case I made any obvious mistakes. I would happily make pull requests to integrate this with the public version of LiteX if we can get this working! This is the output of my adc_test.c program that shows the internal XADC channels working but the two external ones not working:

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 43.187823 °C
VCCINT = 1.4882 V
VCCAUX = 1.792236 V
VCCBRAM = 1.4882 V
Random Sample = 0.0 V
Random Sample = 0.0 V

Files.zip

can you share your C file in github please, I'd prefer not to download random ZIP attachments...

bunnie commented 8 months ago

I'll also note that your analog differential input is mapped to pins C5/C6 which is A0 on the input port of the header. So you could try plugging your test voltage into that pin and seeing if you get any voltage.

I'd also recommend, if you have a voltmeter, to sanity check the actual voltage you're seeing on the headers -- the jumpers you're using for your breadboard are notorious for having unreliable connections, and you're squishing them into the headers pretty tight, so you could also just be a victim of a wobbly connection. The schematics show a resistor divider to ground on all the analog inputs so they would read 0.0V correctly if left floating.

image

An alternative is to just take a jumper and plug it from an analog input to XVREF. Check first with a multimeter but that should just have a 1.25V output on it. You can wire that directly into any of the analog inputs and get a non-zero reading off of it, I think. You should also be able to plug any analog input into a 3.3V line and get effectively 1.0V, which is the full-scale input of the XADC. That would help eliminate wobbly wiring as a culprit as you debug the last mile. Seems like you're getting close!

bunnie commented 8 months ago

image

Just for reference that's also the pin mapping on the board, so, you can line that up against what you're seeing in the Vivado checkpoint viewer to make 100% sure you've got the right ports mapped. The port mapping is tricky, if you're reading the wrong one you'll be getting 0.0V out, at some point in the debug process I remember just reading every voltage out because I couldn't trust my port mappings.

JamesTimothyMeech commented 8 months ago

I attach my xadc.py, digilent_arty.py board and target files, csr.h file and adc_test.c file in a zip file in case I made any obvious mistakes. I would happily make pull requests to integrate this with the public version of LiteX if we can get this working! This is the output of my adc_test.c program that shows the internal XADC channels working but the two external ones not working:

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 43.187823 °C
VCCINT = 1.4882 V
VCCAUX = 1.792236 V
VCCBRAM = 1.4882 V
Random Sample = 0.0 V
Random Sample = 0.0 V

Files.zip

can you share your C file in github please, I'd prefer not to download random ZIP attachments...

Sorry about the zip file! I'll create pull requests on the relevant repositories to show my changes to the Python files after I go ahead and try out the rest of your suggestions! Here is the C file:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <generated/csr.h>

void 
print_double(double x) {
    int i = (int)floor(x);
    double remainder = x - (double) i;
    remainder = remainder * 1000000;
    printf("%i",i);
    printf(".");
    printf("%i",(int) remainder);
}

int main(int argc, char** argv) {
   printf("hello, world\n");
   volatile unsigned int *temperature = (volatile unsigned int *) CSR_XADC_TEMPERATURE_ADDR;
   double temperature_value = *temperature * (503.975/4096) -273.15;
   printf("FPGA Temperature = ");
   print_double(temperature_value);
   printf(" °C\n");

   volatile unsigned int *vccint = (volatile unsigned int *) CSR_XADC_VCCINT_ADDR;
   double vccint_value = (double) *vccint / 4096 * 3;
   printf("VCCINT = ");
   print_double(vccint_value);
   printf(" V\n");

   volatile unsigned int *vccaux = (volatile unsigned int *) CSR_XADC_VCCAUX_ADDR;
   double vccaux_value = (double) *vccaux / 4096.0 * 3.0;
   printf("VCCAUX = ");
   print_double(vccaux_value);
   printf(" V\n");

   volatile unsigned int *vccbram = (volatile unsigned int *) CSR_XADC_VCCBRAM_ADDR;
   double vccbram_value = (double) *vccbram / 4096.0 * 3;
   printf("VCCBRAM = ");
   print_double(vccbram_value);
   printf(" V\n");

   volatile unsigned int *anavpn = (volatile unsigned int *) CSR_XADC_ANAVPVN_ADDR;
   double anavpn_value = (double) *anavpn / 4096.0 * 3;
   printf("Random Sample = ");
   print_double(anavpn_value);
   printf(" V\n");

   volatile unsigned int *vaux4 = (volatile unsigned int *) CSR_XADC_VAUX4_ADDR;
   double vaux4_value = (double) *vaux4 / 4096.0 * 3;
   printf("Random Sample = ");
   print_double(vaux4_value);
   printf(" V\n");

   return 0;
}
JamesTimothyMeech commented 8 months ago

I'll also note that your analog differential input is mapped to pins C5/C6 which is A0 on the input port of the header. So you could try plugging your test voltage into that pin and seeing if you get any voltage.

I'd also recommend, if you have a voltmeter, to sanity check the actual voltage you're seeing on the headers -- the jumpers you're using for your breadboard are notorious for having unreliable connections, and you're squishing them into the headers pretty tight, so you could also just be a victim of a wobbly connection. The schematics show a resistor divider to ground on all the analog inputs so they would read 0.0V correctly if left floating.

image

An alternative is to just take a jumper and plug it from an analog input to XVREF. Check first with a multimeter but that should just have a 1.25V output on it. You can wire that directly into any of the analog inputs and get a non-zero reading off of it, I think. You should also be able to plug any analog input into a 3.3V line and get effectively 1.0V, which is the full-scale input of the XADC. That would help eliminate wobbly wiring as a culprit as you debug the last mile. Seems like you're getting close!

I changed my hardware setup to the one you suggest (see attached photo) and also measured the A0 voltage on the back of the board where the header is soldered in. Unfortunately I still got 0.0 V in my program print out:

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 40.603967 °C
VCCINT = 1.4882 V
VCCAUX = 1.792968 V
VCCBRAM = 1.4150 V
Random Sample = 0.0 V
Random Sample = 0.0 V

Image Here are the links to the pull requests with the Python files: xadc.py: https://github.com/enjoy-digital/litex/blob/8b382c05f7409cb7d423e877f14025a140db197d/litex/soc/cores/xadc.py digilent_arty.py platform: https://github.com/litex-hub/litex-boards/blob/328a897b3d1df826acde5dd4d27a05b2c85aa615/litex_boards/platforms/digilent_arty.py digilent_arty.py target: https://github.com/litex-hub/litex-boards/blob/328a897b3d1df826acde5dd4d27a05b2c85aa615/litex_boards/targets/digilent_arty.py I will now try making CSRs for all the analog pins that are broken out to the header and reading them all at once!

JamesTimothyMeech commented 8 months ago

image

this is what mine looks like. IBUF is what I'm using, and if you click on it you can confirm what pin it's mapped to.

I can confirm that the pin mappings look right for me for the analog pins Screenshot from 2024-01-09 11-41-50 Screenshot from 2024-01-09 11-41-34

JamesTimothyMeech commented 8 months ago

Hello @bunnie @enjoy-digital thanks for all your helpful comments, I finally managed to measure a voltage on VAUX0! The voltage shows up on pin A5 on the header which seems consistent with the schematic: https://digilent.com/reference/_media/programmable-logic/arty-a7/arty-a7-e2-sch.pdf I forgot to recompile the boot.bin for LiteOS when I generated new gateware and recompiled adc_test.c. This could have caused problems with the CSRs.

liteOS> run adc_test.elf
hello, world
FPGA Temperature = 42.203497 °C
ANAVPVP = 0.0 V
CSR_XADC_VAUX0 = 1.138183 V
CSR_XADC_VAUX4 = 0.0 V
CSR_XADC_VAUX5 = 0.0 V
CSR_XADC_VAUX6 = 0.0 V
CSR_XADC_VAUX7 = 0.0 V
CSR_XADC_VAUX15 = 0.0 V
VCCINT = 1.4150 V
VCCAUX = 1.792236 V
VCCBRAM = 1.4882 V

I updated the pull requests on litex: https://github.com/enjoy-digital/litex/pull/1872/files and litex_boards: https://github.com/litex-hub/litex-boards/pull/557 with what I have locally. It isn't clean enough to merge yet but I would be interested to hear what you would require for me to be able to merge it and your opinions on where I have put the pin definitions and XADC specific code. I can't get voltages to show up on the VAUX CSRs that my program prints no matter where I place the XREF voltage in the header A0-A11 but I will leave that mystery for another day.

bunnie commented 8 months ago

That's great! congrats. I can't comment on what it takes to get something to a PR level, but, I can give some pointers on the other voltages. In order for them to read out, you have to configure the XADC to sample them. If you haven't become intimately familiar with it yet, you want to read through UG480, in particular: https://docs.xilinx.com/r/en-US/ug480_7Series_XADC/Sequencer-Modes

The XADC is actually a single that has a multiplexer for multiple channels. So in order for you to read more than one channel, you have to set up its sequencer and let it know which channels you want it to read. The reason they don't just have it read all channels is that with 16 channels, you would get 1/16th the theoretical sampling rate if you forced it to read all the channels all the time. So if your application only uses two channels you can have it ping pong between the two channels and cut down your max sampling rate to only half.

Unfortunately, I don't have a compact and easy description of the sequencer for you. Again, this was something I had to grunt through. I did the "hard way" and implemented it as a hardware state machine because I needed the XADC to run autonomously without CPU intervention for my application. However, there's probably two things I can point you to that help you on your journey.

First, here is a possible cheat sheet for what you need to do:

https://github.com/betrusted-io/gateware/blob/77fbf27124c50c838149707a0faa38eb366be36a/gateware/trng/trng_managed.py#L1528-L1537

Copied here for easy discussion:

            sense_table = {
                0: 0x410EF0,  # set sequencer default mode -- allows updating of Sequencer
                1: 0x484701,  # Seq 0: Aux Int Bram Temp Cal
                2: 0x49D870,  # Seq 1: Vaux15 (usbn) Vaux14 (usbp) Vaux12 (noise0) Vaux11 (gpio2) Vaux6 (vbus) Vaux5 (gpio5) Vaux4 (noise1)
                3: 0x4A4701,  # Average aux int bram temp cal
                4: 0x4BC860,  # Average all but noise (c040)
                5: 0x412EF0,
                6: 0x409000,  # Average by 16 samples
                7: 0x420420,
            }

Each entry in the table is 24 bits wide; bits [23:16] are the address of the XADC control register you want to hit, and the bottom [15:0] bits are the value to slot in.

So, looking at the table, the 0th value is 0x410EF0, which is write to register 0x41 the value 0x0EF0:

image

The 2nd value is 0x49D870 (I'm skipping the 1st value, you can look it up), which means write 0xD870 to register 0x49.

image

0xD870 = 1101_1000_1110_0000

So I'm picking Aux 15, Aux 14, Aux 12, Aux 11, Aux 7, Aux6, and Aux5 with this bitmask. This means I'm sampling 7 out of the 16 possible inputs.

Likely, you have to configure that bitmask, at the very least, to include the AUX channels you want to read, as well as the other words in that lookup table -- they all have a role, like setting the sequencer mode and averaging, etc.

Second, you might be able to get away with just jamming the values in as compile-time parameters, instead of writing them with software:

https://github.com/betrusted-io/gateware/blob/77fbf27124c50c838149707a0faa38eb366be36a/gateware/trng/trng_managed.py#L1352-L1361

This will make the device more ready "out of the box". You will have to replace those parameters with the ones that match your application, the exact parameters I put there match my needs for betrusted SOC on boot, which is very specific (I need the TRNG to work, and nothing else).

Good luck!

JamesTimothyMeech commented 8 months ago

Thanks for the information, I might be able to get by with the functionality I have right now but I'll dive into this if I need to change something or sample the other channels!

JamesTimothyMeech commented 8 months ago

I edited the code to have the ADC calibrated but to avoid adding any of the other channels to the sequencer to increase my sampling speed (p_INIT_48 = 0x0001). I also changed it to sample auxiliary channel 12 (p_INIT_49 = 0x1000) because I wanted to use a channel without a potential divider on it. This saved me from my original plan of attacking my Arty with the soldering iron to remove the potential divider! I also removed all averaging except for the calibration by setting (p_INIT_4A = 0x0001 and p_INIT_4B = 0x0000)

p_INIT_48 = 0x0001
p_INIT_49 = 0x1000
p_INIT_4A = 0x0001
p_INIT_4B = 0x0000

I get a couple of Vivado critical warnings when I connect up vaux12 but when I open the .dcp file of the design vaux12 seems to be attached to the XADC block correctly.

CRITICAL WARNING: [Vivado 12-1411] Cannot set LOC property of ports, Could not legally place instance analog_vaux12_n_IBUF_inst at B7 (IOB_X1Y146) since it belongs to a shape containing instance XADC. The shape requires relative placement between analog_vaux12_n_IBUF_inst and XADC that can not be honoured because it would result in an invalid location for XADC. [/home/james/Desktop/Casino/FPGA-System-on-Chip-Firmware/LiteX/build/digilent_arty/gateware/digilent_arty.xdc:65]
CRITICAL WARNING: [Vivado 12-1411] Cannot set LOC property of ports, Could not legally place instance analog_vaux12_p_IBUF_inst at B6 (IOB_X1Y145) since it belongs to a shape containing instance XADC. The shape requires relative placement between analog_vaux12_p_IBUF_inst and XADC that can not be honoured because it would result in an invalid location for XADC. [/home/james/Desktop/Casino/FPGA-System-on-Chip-Firmware/LiteX/build/digilent_arty/gateware/digilent_arty.xdc:69]

Screenshot from 2024-01-13 16-39-00 Also my code runs and allows me to sample the signals I have connected to the A6 and A7 pins which connect to the vaux12 channel.

Despite the warnings this seems to working as expected!