Friday, July 14, 2017

ARM Cortex SVD files

A lot of goodness can be represented by an SVD file.

The full name is CMSIS-SVD and was designed by Keil who was acquired by ARM. All reasonable ARM Cortex vendors these days who want users to program their targets have SVD files. The scope of the SVD file covers all the peripherals in the designs down to the numerical addresses.

Here is an extract from the STM32L432's SVD file. We see the DAC is at a baseAddress of 0x40007400 and the first reg in the DAC is the CR or control reg at offset 0, further, we see the individual fields of the CR with EN1 at bit position 0.

  <peripherals>
    <peripheral>
      <name>DAC1</name>
      <description>Digital-to-analog converter</description>
      <groupName>DAC</groupName>
      <baseAddress>0x40007400</baseAddress>
      <addressBlock>
        <offset>0x0</offset>
        <size>0x400</size>
        <usage>registers</usage>
      </addressBlock>
      <registers>
        <register>
          <name>CR</name>
          <displayName>CR</displayName>
          <description>control register</description>
          <addressOffset>0x0</addressOffset>
          <size>0x20</size>
          <access>read-write</access>
          <resetValue>0x00000000</resetValue>
          <fields>
            <field>
              <name>EN1</name>
              <description>DAC channel1 enable</description>
              <bitOffset>0</bitOffset>
              <bitWidth>1</bitWidth>
            </field>
      etc

This is great info! Can we use it to make our life as programmers smoother? As an embedded programmer I use ST cores and GDB to debug them. I have put together a couple of Ruby 2.x scripts that suck in the SVD file and emit a GDB script. The first Ruby script generates this:

set pagination off
set logging overwrite on
set logging file DAC1.log
set logging on
set logging redirect on
x/256x 0x40007400
set logging redirect off
set logging off
set logging file DMA1.log
set logging on
set logging redirect on
x/256x 0x40020000
set logging redirect off
set logging off

etc

Now in GDB you just say:

source filename 

Where filename is the output of the first Ruby script. So that's great, now we have a binary file of data for each peripheral in the SVD file as realized from our target processor when we stopped it.

It would be great if we could interpret the binary data in those dumps in a form that's human readable and can be diff'ed from one capture to another. This technique works well for binary blobs also if you can break in. You just run the next script and look at what changed in the controller. So lets look at a use case with the ADC (same part STM32L432KC):


0x50040000:     0x0000007f      0x00000000      0x10000005      0xfffe2000
0x50040010:     0x00000000      0x00028000      0x00000000      0x00000000
0x50040020:     0x0fff0000      0x00ff0000      0x00ff0000      0x00000000
0x50040030:     0x00000140      0x00000000      0x00000000      0x00000000
0x50040040:     0x00000231      0x00000000      0x00000000      0x00000000
0x50040050:     0x00000000      0x00000000      0x00000000      0x00000000
0x50040060:     0x00000000      0x00000000      0x00000000      0x00000000
0x50040070:     0x00000000      0x00000000      0x00000000      0x00000000
0x50040080:     0x00000264      0x00000000      0x00000000      0x00000000
0x50040090:     0x00000000      0x00000000      0x00000000      0x00000000
0x500400a0:     0x00000000      0x00000000      0x00000000      0x00000000
0x500400b0:     0x00000000      0x0000003a      0x00000000


After the second script:

ADC.ISR.JQOVF: 0
ADC.ISR.AWD3: 0
ADC.ISR.AWD2: 0
ADC.ISR.AWD1: 0
ADC.ISR.JEOS: 1
ADC.ISR.JEOC: 1
ADC.ISR.OVR: 1
ADC.ISR.EOS: 1
ADC.ISR.EOC: 1
ADC.ISR.EOSMP: 1
ADC.ISR.ADRDY: 1
ADC.IER.JQOVFIE: 0
ADC.IER.AWD3IE: 0
ADC.IER.AWD2IE: 0
ADC.IER.AWD1IE: 0
ADC.IER.JEOSIE: 0
ADC.IER.JEOCIE: 0
ADC.IER.OVRIE: 0
ADC.IER.EOSIE: 0
ADC.IER.EOCIE: 0
ADC.IER.EOSMPIE: 0
ADC.IER.ADRDYIE: 0
ADC.CR.ADCAL: 0
ADC.CR.ADCALDIF: 0
ADC.CR.DEEPPWD: 0
ADC.CR.ADVREGEN: 1
ADC.CR.JADSTP: 0
ADC.CR.ADSTP: 0
ADC.CR.JADSTART: 0
ADC.CR.ADSTART: 1
ADC.CR.ADDIS: 0
ADC.CR.ADEN: 1
ADC.CFGR.AWDCH1CH[4:0]: 0x1f
ADC.CFGR.JAUTO: 1
ADC.CFGR.JAWD1EN: 1
ADC.CFGR.AWD1EN: 1
ADC.CFGR.AWD1SGL: 1
ADC.CFGR.JQM: 1
ADC.CFGR.JDISCEN: 1
ADC.CFGR.DISCNUM[2:0]: 7
ADC.CFGR.DISCEN: 0
ADC.CFGR.AUTOFF: 0
ADC.CFGR.AUTDLY: 0
ADC.CFGR.CONT: 1
ADC.CFGR.OVRMOD: 0
ADC.CFGR.EXTEN[1:0]: 0
ADC.CFGR.EXTSEL[3:0]: 0
ADC.CFGR.ALIGN: 0
ADC.CFGR.RES[1:0]: 0
ADC.CFGR.DMACFG: 0
ADC.CFGR.DMAEN: 0
ADC.CFGR2.ROVSM: 0
ADC.CFGR2.TOVS: 0
ADC.CFGR2.OVSS[3:0]: 0
ADC.CFGR2.OVSR[2:0]: 0
ADC.CFGR2.JOVSE: 0
ADC.CFGR2.ROVSE: 0
ADC.SMPR1.SMP9[2:0]: 0
ADC.SMPR1.SMP8[2:0]: 0
ADC.SMPR1.SMP7[2:0]: 0
ADC.SMPR1.SMP6[2:0]: 0
ADC.SMPR1.SMP5[2:0]: 5
ADC.SMPR1.SMP4[2:0]: 0
ADC.SMPR1.SMP3[2:0]: 0
ADC.SMPR1.SMP2[2:0]: 0
ADC.SMPR1.SMP1[2:0]: 0
ADC.SMPR1.SMP0[2:0]: 0
ADC.SMPR2.SMP18[2:0]: 0
ADC.SMPR2.SMP17[2:0]: 0
ADC.SMPR2.SMP16[2:0]: 0
ADC.SMPR2.SMP15[2:0]: 0
ADC.SMPR2.SMP14[2:0]: 0
ADC.SMPR2.SMP13[2:0]: 0
ADC.SMPR2.SMP12[2:0]: 0
ADC.SMPR2.SMP11[2:0]: 0
ADC.SMPR2.SMP10[2:0]: 0
ADC.TR1.HT1[11:0]: 0xfff
ADC.TR1.LT1[11:0]: 0
ADC.TR2.HT2[7:0]: 0xff
ADC.TR2.LT2[7:0]: 0
ADC.TR3.HT3[7:0]: 0xff
ADC.TR3.LT3[7:0]: 0
ADC.SQR1.SQ4[4:0]: 0
ADC.SQR1.SQ3[4:0]: 0
ADC.SQR1.SQ2[4:0]: 0
ADC.SQR1.SQ1[4:0]: 5
ADC.SQR1.L3[3:0]: 0
ADC.SQR2.SQ9[4:0]: 0
ADC.SQR2.SQ8[4:0]: 0
ADC.SQR2.SQ7[4:0]: 0
ADC.SQR2.SQ6[4:0]: 0
ADC.SQR2.SQ5[4:0]: 0
ADC.SQR3.SQ14[4:0]: 0
ADC.SQR3.SQ13[4:0]: 0
ADC.SQR3.SQ12[4:0]: 0
ADC.SQR3.SQ11[4:0]: 0
ADC.SQR3.SQ10[4:0]: 0
ADC.SQR4.SQ16[4:0]: 0
ADC.SQR4.SQ15[4:0]: 0
ADC.DR.regularDATA[15:0]: 0x231
ADC.JSQR.JSQ4[4:0]: 0
ADC.JSQR.JSQ3[4:0]: 0
ADC.JSQR.JSQ2[4:0]: 0
ADC.JSQR.JSQ1[4:0]: 0
ADC.JSQR.JEXTEN[1:0]: 0
ADC.JSQR.JEXTSEL[3:0]: 0
ADC.JSQR.JL[1:0]: 0
ADC.OFR1.OFFSET1_EN: 0
ADC.OFR1.OFFSET1_CH[4:0]: 0
ADC.OFR1.OFFSET1[11:0]: 0
ADC.OFR2.OFFSET2_EN: 0
ADC.OFR2.OFFSET2_CH[4:0]: 0
ADC.OFR2.OFFSET2[11:0]: 0
ADC.OFR3.OFFSET3_EN: 0
ADC.OFR3.OFFSET3_CH[4:0]: 0
ADC.OFR3.OFFSET3[11:0]: 0
ADC.OFR4.OFFSET4_EN: 0
ADC.OFR4.OFFSET4_CH[4:0]: 0
ADC.OFR4.OFFSET4[11:0]: 0
ADC.JDR1.JDATA1[15:0]: 0x264
ADC.JDR2.JDATA2[15:0]: 0
ADC.JDR3.JDATA3[15:0]: 0
ADC.JDR4.JDATA4[15:0]: 0
ADC.AWD2CR.AWD2CH[17:0]: 0
ADC.AWD3CR.AWD3CH[17:0]: 0
ADC.DIFSEL.DIFSEL_1_15[14:0]: 0
ADC.DIFSEL.DIFSEL_16_18[2:0]: 0
ADC.CALFACT.CALFACT_D[6:0]: 0
ADC.CALFACT.CALFACT_S[6:0]: 0x3a

So now we can study the regs with symbolic output when we consult the datasheets for these types of controllers.

The Ruby scripts are here:

https://github.com/morbos/ruby


No comments:

Post a Comment