summaryrefslogtreecommitdiffstats
path: root/utils/regtools
diff options
context:
space:
mode:
Diffstat (limited to 'utils/regtools')
-rw-r--r--utils/regtools/Makefile5
-rw-r--r--utils/regtools/desc/regs-example-v1.xml91
-rw-r--r--utils/regtools/desc/regs-example.xml153
-rw-r--r--utils/regtools/desc/spec-1.0.txt (renamed from utils/regtools/desc/XML.txt)0
-rw-r--r--utils/regtools/desc/spec-2.0.txt373
-rw-r--r--utils/regtools/headergen_v1.cpp (renamed from utils/regtools/headergen.cpp)6
-rw-r--r--utils/regtools/include/soc_desc.hpp384
-rw-r--r--utils/regtools/include/soc_desc_v1.hpp (renamed from utils/regtools/lib/soc_desc.hpp)32
-rw-r--r--utils/regtools/lib/Makefile9
-rw-r--r--utils/regtools/lib/formula.cpp214
-rw-r--r--utils/regtools/lib/soc_desc.cpp1530
-rw-r--r--utils/regtools/lib/soc_desc_v1.cpp990
-rw-r--r--utils/regtools/qeditor/backend.cpp4
-rw-r--r--utils/regtools/qeditor/backend.h6
-rw-r--r--utils/regtools/qeditor/mainwindow.cpp4
-rw-r--r--utils/regtools/qeditor/qeditor.pro4
-rw-r--r--utils/regtools/qeditor/regedit.cpp6
-rw-r--r--utils/regtools/qeditor/utils.cpp2
-rw-r--r--utils/regtools/swiss_knife.cpp612
-rw-r--r--utils/regtools/tester_v1.cpp (renamed from utils/regtools/tester.cpp)16
20 files changed, 3735 insertions, 706 deletions
diff --git a/utils/regtools/Makefile b/utils/regtools/Makefile
index 5e3feafd01..fed5d8c8e5 100644
--- a/utils/regtools/Makefile
+++ b/utils/regtools/Makefile
@@ -2,8 +2,9 @@ DEFINES=
CC?=gcc
CXX?=g++
LD?=g++
-CFLAGS=-g -std=c99 -Wall $(DEFINES) -Ilib
-CXXFLAGS=-g -Wall $(DEFINES) -Ilib
+INCLUDE=-Iinclude/
+CFLAGS=-g -std=c99 -Wall $(DEFINES) $(INCLUDE)
+CXXFLAGS=-g -Wall $(DEFINES) $(INCLUDE)
LDFLAGS=-Llib -lsocdesc `xml2-config --libs`
SRC=$(wildcard *.c)
SRCXX=$(wildcard *.cpp)
diff --git a/utils/regtools/desc/regs-example-v1.xml b/utils/regtools/desc/regs-example-v1.xml
new file mode 100644
index 0000000000..4f3cf81ff2
--- /dev/null
+++ b/utils/regtools/desc/regs-example-v1.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<soc name="imx233" desc="i.MX233">
+ <dev name="APBH" long_name="APHB DMA" desc="AHB-to-APBH Bridge with DMA" version="3.2.0">
+ <addr name="APBH" addr="0x80004000"/>
+ <reg name="CTRL0" desc="" sct="yes">
+ <addr name="CTRL0" addr="0x0"/>
+ <field name="SFTRST" desc="" bitrange="31:31"/>
+ <field name="CLKGATE" desc="" bitrange="30:30"/>
+ <field name="AHB_BURST8_EN" desc="" bitrange="29:29"/>
+ <field name="APB_BURST4_EN" desc="" bitrange="28:28"/>
+ <field name="RSVD0" desc="" bitrange="27:24"/>
+ <field name="RESET_CHANNEL" desc="" bitrange="23:16">
+ <value name="SSP1" value="0x2" desc=""/>
+ <value name="SSP2" value="0x4" desc=""/>
+ <value name="ATA" value="0x10" desc=""/>
+ <value name="NAND0" value="0x10" desc=""/>
+ <value name="NAND1" value="0x20" desc=""/>
+ <value name="NAND2" value="0x40" desc=""/>
+ <value name="NAND3" value="0x80" desc=""/>
+ </field>
+ <field name="CLKGATE_CHANNEL" desc="" bitrange="15:8">
+ <value name="SSP1" value="0x2" desc=""/>
+ <value name="SSP2" value="0x4" desc=""/>
+ <value name="ATA" value="0x10" desc=""/>
+ <value name="NAND0" value="0x10" desc=""/>
+ <value name="NAND1" value="0x20" desc=""/>
+ <value name="NAND2" value="0x40" desc=""/>
+ <value name="NAND3" value="0x80" desc=""/>
+ </field>
+ <field name="FREEZE_CHANNEL" desc="" bitrange="7:0">
+ <value name="SSP1" value="0x2" desc=""/>
+ <value name="SSP2" value="0x4" desc=""/>
+ <value name="ATA" value="0x10" desc=""/>
+ <value name="NAND0" value="0x10" desc=""/>
+ <value name="NAND1" value="0x20" desc=""/>
+ <value name="NAND2" value="0x40" desc=""/>
+ <value name="NAND3" value="0x80" desc=""/>
+ </field>
+ </reg>
+ <reg name="CTRL1" desc="" sct="yes">
+ <addr name="CTRL1" addr="0x10"/>
+ <field name="RSVD1" desc="" bitrange="31:24"/>
+ <field name="CH_CMDCMPLT_IRQ_EN" desc="" bitrange="23:16"/>
+ <field name="RSVD0" desc="" bitrange="15:8"/>
+ <field name="CH_CMDCMPLT_IRQ" desc="" bitrange="7:0"/>
+ </reg>
+ <reg name="CHn_CMD" desc="">
+ <formula string="0x60+n*0x70"/>
+ <addr name="CH0_CMD" addr="0x60"/>
+ <addr name="CH1_CMD" addr="0xd0"/>
+ <addr name="CH2_CMD" addr="0x140"/>
+ <addr name="CH3_CMD" addr="0x1b0"/>
+ <addr name="CH4_CMD" addr="0x220"/>
+ <addr name="CH5_CMD" addr="0x290"/>
+ <addr name="CH6_CMD" addr="0x300"/>
+ <addr name="CH7_CMD" addr="0x370"/>
+ <field name="XFER_COUNT" desc="" bitrange="31:16"/>
+ <field name="CMDWORDS" desc="" bitrange="15:12"/>
+ <field name="RSVD1" desc="" bitrange="11:9"/>
+ <field name="HALTONTERMINATE" desc="" bitrange="8:8"/>
+ <field name="WAIT4ENDCMD" desc="" bitrange="7:7"/>
+ <field name="SEMAPHORE" desc="" bitrange="6:6"/>
+ <field name="NANDWAIT4READY" desc="" bitrange="5:5"/>
+ <field name="NANDLOCK" desc="" bitrange="4:4"/>
+ <field name="IRQONCMPLT" desc="" bitrange="3:3"/>
+ <field name="CHAIN" desc="" bitrange="2:2"/>
+ <field name="COMMAND" desc="" bitrange="1:0">
+ <value name="NO_DMA_XFER" value="0x0" desc=""/>
+ <value name="DMA_WRITE" value="0x1" desc=""/>
+ <value name="DMA_READ" value="0x2" desc=""/>
+ <value name="DMA_SENSE" value="0x3" desc=""/>
+ </field>
+ </reg>
+ </dev>
+ <dev name="SAIF" long_name="Sync Audio Interface" desc="Sync Audio Interface (SAIF)" version="3.2.0">
+ <addr name="SAIF1" addr="0x80042000"/>
+ <addr name="SAIF2" addr="0x80046000"/>
+ <reg name="DATA" desc="" sct="yes">
+ <addr name="DATA" addr="0x20"/>
+ <field name="PCM_RIGHT" desc="" bitrange="31:16"/>
+ <field name="PCM_LEFT" desc="" bitrange="15:0"/>
+ </reg>
+ <reg name="VERSION" desc="">
+ <addr name="VERSION" addr="0x30"/>
+ <field name="MAJOR" desc="" bitrange="31:24"/>
+ <field name="MINOR" desc="" bitrange="23:16"/>
+ <field name="STEP" desc="" bitrange="15:0"/>
+ </reg>
+ </dev>
+</soc>
+
diff --git a/utils/regtools/desc/regs-example.xml b/utils/regtools/desc/regs-example.xml
new file mode 100644
index 0000000000..6fb8f759fe
--- /dev/null
+++ b/utils/regtools/desc/regs-example.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+<soc version="2">
+ <name>vsoc</name>
+ <title>Virtual SOC</title>
+ <desc>Virtual SoC is a nice and powerful chip.</desc>
+ <author>Amaury Pouly</author>
+ <isa>ARM</isa>
+ <version>0.5</version>
+ <node>
+ <name>int</name>
+ <title>Interrupt Collector</title>
+ <desc>The interrupt collector controls the routing of interrupt to the processor</desc>
+ <instance>
+ <name>ICOLL</name>
+ <title>Interrupt collector</title>
+ <address>0x80000000</address>
+ </instance>
+ <node>
+ <name>status</name>
+ <access>read-only</access>
+ <title>Interrupt status register</title>
+ <instance>
+ <name>STATUS</name>
+ <address>0x4</address>
+ </instance>
+ <register>
+ <width>8</width>
+ <field>
+ <name>VDDIO_BO</name>
+ <desc>VDDIO brownout interrupt status</desc>
+ <position>0</position>
+ </field>
+ </register>
+ </node>
+ <node>
+ <name>enable</name>
+ <title>Interrupt enable register</title>
+ <instance>
+ <name>ENABLE</name>
+ <address>0x8</address>
+ </instance>
+ <register>
+ <width>16</width>
+ <field>
+ <name>VDDIO_BO</name>
+ <desc>VDDIO brownout interrupt enable</desc>
+ <position>0</position>
+ <width>2</width>
+ <enum>
+ <name>DISABLED</name>
+ <desc>Interrupt is disabled</desc>
+ <value>0</value>
+ </enum>
+ <enum>
+ <name>ENABLED</name>
+ <desc>Interrupt is enabled</desc>
+ <value>1</value>
+ </enum>
+ <enum>
+ <name>NMI</name>
+ <desc>Interrupt is non-maskable</desc>
+ <value>2</value>
+ </enum>
+ </field>
+ </register>
+ <variant>
+ <type>set</type>
+ <offset>4</offset>
+ </variant>
+ <variant>
+ <type>clr</type>
+ <offset>8</offset>
+ </variant>
+ </node>
+ </node>
+ <node>
+ <name>gpio</name>
+ <title>GPIO controller</title>
+ <desc>A GPIO controller manages several ports</desc>
+ <instance>
+ <name>CPU_GPIO</name>
+ <title>CPU GPIO controller 1 through 3</title>
+ <range>
+ <first>1</first>
+ <count>3</count>
+ <formula variable="n">0x80001000+(n-1)*0x1000</formula>
+ </range>
+ </instance>
+ <instance>
+ <name>COP_GPIO</name>
+ <title>Companion processor GPIO controller</title>
+ <desc>Although the companion processor GPIO controller is accessible from the CPU, it incurs an extra penalty on the bus</desc>
+ <address>0x90000000</address>
+ </instance>
+ <node>
+ <name>port</name>
+ <title>GPIO port</title>
+ <instance>
+ <name>PORT</name>
+ <range>
+ <first>0</first>
+ <count>4</count>
+ <base>0</base>
+ <stride>0x100</stride>
+ </range>
+ </instance>
+ <node>
+ <name>input</name>
+ <title>Input register</title>
+ <instance>
+ <name>IN</name>
+ <address>0</address>
+ </instance>
+ <register>
+ <width>8</width>
+ <field>
+ <name>VALUE</name>
+ <position>0</position>
+ <width>8</width>
+ </field>
+ </register>
+ </node>
+ <node>
+ <name>output_enable</name>
+ <title>Output enable register</title>
+ <instance>
+ <name>OE</name>
+ <address>0x10</address>
+ </instance>
+ <register>
+ <width>8</width>
+ <field>
+ <name>ENABLE</name>
+ <position>0</position>
+ <width>8</width>
+ </field>
+ </register>
+ <variant>
+ <type>set</type>
+ <address>4</address>
+ </variant>
+ <variant>
+ <type>clr</type>
+ <address>8</address>
+ </variant>
+ <variant>
+ <type>mask</type>
+ <address>12</address>
+ </variant>
+ </node>
+ </node>
+ </node>
+</soc> \ No newline at end of file
diff --git a/utils/regtools/desc/XML.txt b/utils/regtools/desc/spec-1.0.txt
index 3c28154a1f..3c28154a1f 100644
--- a/utils/regtools/desc/XML.txt
+++ b/utils/regtools/desc/spec-1.0.txt
diff --git a/utils/regtools/desc/spec-2.0.txt b/utils/regtools/desc/spec-2.0.txt
new file mode 100644
index 0000000000..79b9f6be44
--- /dev/null
+++ b/utils/regtools/desc/spec-2.0.txt
@@ -0,0 +1,373 @@
+This file describes the format of the register map based on XML, version 2.0.
+
+1) Overview
+-----------
+
+1.1) Nodes and instances
+------------------------
+
+This specification is based on the concept of "nodes". Nodes are containers
+which can contain other nodes and/or a register. Each node can have one or more
+addresses (addresses are always relative to the parent node). The idea is that
+this hierarchy of nodes generates a number of addresses recursively. The example
+below outlines this idea:
+
+<node>
+ <name>N</name>
+ <instance>
+ <name>A</name>
+ <address>X</address>
+ </instance>
+ <instance>
+ <name>B</name>
+ <address>Y</address>
+ </instance>
+ <!-- HERE -->
+</node>
+
+This example creates one node named N and two instances named A and B,
+at respective addresses X and Y. This means that all subnodes of this node will
+have two copies: one relative to X, which path will be prefixed by "A", and
+one relative to Y, which path will be prefixed by "B".
+This example below explores this idea in details:
+
+ <!-- HERE -->
+ <node>
+ <name>S_N</name>
+ <instance>
+ <name>C</name>
+ <address>U</address>
+ </instance>
+ <instance>
+ <name>D</name>
+ <address>V</address>
+ </instance>
+ </node>
+
+In this example, N generates two copies of the sub-node S_N.
+The sub-node S_N generates two instances C and D. The whole hierarchy thus generates
+four instances:
+- A.C at X+U
+- A.D at X+V
+- B.C at Y+U
+- B.D at Y+V
+
+As a note for later, notice that there really are two hierarchies in parallel:
+- the node hierarchy: it is composed of N and N.S_N
+- the instance hierarchy: it is made up of A, B, A.C, A.D, B.C and B.D
+
+1.2) Ranges
+-----------
+
+To make things more useful, in particular in case of multiple copies of a register,
+we introduce the concept of range addresses with an example:
+
+<node>
+ <name>N</name>
+ <instance>
+ <name>A</name>
+ <range>
+ <first>1</first>
+ <count>5</count>
+ <base>0x1000</base>
+ <stride>0x100</stride>
+ </range>
+ </instance>
+ <node>
+ <name>NN</name>
+ <instance>
+ <name>E</name>
+ <address>0x4</address>
+ </instance>
+ </node>
+</node>
+
+A range describes a contiguous set of adresses, indexed by a number. One can
+specify the first number in the range, and the number of copies. In the case
+of a regular pattern (base + n * stride), we can specify a stride
+to compute the address of the next copy. In this example, the top-level
+nodes generates five copies which path is A[1], A[2], ..., A[5]
+and which addresses are 0x1000+1*0x100, 0x1000+2*0x100, ..., 0x1000+5*0x100.
+If we add the sub-node to the picture, for each copy we create a instance E
+we offset 0x4 from the parent. Overall this generates 5 instances:
+- A[1].E at 0x1000+1*0x100+0x4 = 0x1104
+- A[2].E at 0x1000+2*0x100+0x4 = 0x1204
+- A[3].E at 0x1000+3*0x100+0x4 = 0x1304
+- A[4].E at 0x1000+4*0x100+0x4 = 0x1404
+- A[5].E at 0x1000+5*0x100+0x4 = 0x1504
+Note that the intermediate path also define instances, so there are 5 additional
+instances in reality:
+- A[1] at 0x1100
+- A[2] at 0x1200
+- A[3] at 0x1300
+- A[4] at 0x1400
+- A[5] at 0x1500
+
+For the record, there is a more general way of specifying a range when it does
+not follow a nice regular pattern. One can specify a formula where the parameter
+is the index. There are no restrictions on the formula except that it must use
+usual arithmetic operators. The example below illustrate such a use:
+
+<node>
+ <name>N</name>
+ <instance>
+ <name>F</name>
+ <range>
+ <first>0</first>
+ <count>4</count>
+ <formula variable="n">0x50+(n/2)*0x100+(n%2)*0x10</formula>
+ </range>
+ </instance>
+</node>
+
+In this example we generate four nodes F[0], ..., F[3] with a formula. Here "/"
+is the euclidian division and "%" is the modulo operator. Note the use of an
+attribute to specify which variable represents the index. The generated addresses
+are:
+- F[0] at 0x50+(0/2)*0x100+(0%2)*0x10 = 0x50
+- F[1] at 0x50+(1/2)*0x100+(1%2)*0x10 = 0x50+0x10 = 0x60
+- F[2] at 0x50+(2/2)*0x100+(2%2)*0x10 = 0x50+0x100 = 0x150
+- F[3] at 0x50+(3/2)*0x100+(3%2)*0x10 = 0x50+0x100+0x10 = 0x160
+
+1.3) Node description
+---------------------
+
+For documentation purposes, node can of course carry some description, as well
+as instances. More precisely, nodes can have a title, that is a short description
+very much like a chapter title, and a description, this is a free form and potentially
+lengthy description of the node. Instances too can have a title and a description.
+The following example illustrates this:
+
+<node>
+ <name>icoll</name>
+ <title>DMA Controller</title>
+ <desc>The DMA controller provides uniform DMA facilities to transfer data from
+ and to peripherals. It uses memory-mapped tables and support chained
+ transfers.</desc>
+ <instance>
+ <name>AHB_DMAC</name>
+ <address>0x80000000</address>
+ <title>AHB DMA Controller</title>
+ <desc>The AHB DMA controller provides DMA facilities for the peripherals
+ on the AHB bus like the SSP and PIX engines.</desc>
+ </instance>
+ <instance>
+ <name>APB_DMAC</name>
+ <address>0x8001000</address>
+ <title>APB DMA Controller</title>
+ <desc>The APB DMA controller provides DMA facilities for the peripherals
+ on the APB bus like the I2C and PCM engines.</desc>
+ </instance>
+</node>
+
+1.4) Register description
+--------------------------
+
+The goal of the register description is of course to describe registers!
+To see how registers relate to the node hierarchy, see 1.5, this section focuses
+only the description only.
+
+A register carries a lot of information, which is organise logically. A register
+can have a width, in bits. By default registers are assumed to be 32-bit wide.
+The most useful feature of register description is to describe the fields of
+the registers. Each field has a name, a start position and a width. Fields
+can also carry a description. Finally, each field can specify enumerated values,
+that is named values, for convenience. Enumerated values have a name, a value
+and an optional description. The example below illustrates all these concepts:
+
+<register>
+ <width>8</width>
+ <field>
+ <name>MODE</name>
+ <desc>Interrupt mode</desc>
+ <position>0</position>
+ <width>2</width>
+ <enum>
+ <name>DISABLED</name>
+ <desc>Interrupt is disabled</desc>
+ <value>0</value>
+ </enum>
+ <enum>
+ <name>ENABLED</name>
+ <desc>Interrupt is enabled</desc>
+ <value>1</value>
+ </enum>
+ <enum>
+ <name>NMI</name>
+ <desc>Interrupt is non-maskable</desc>
+ <value>2</value>
+ </enum>
+ </field>
+ <field>
+ <name>PRIORITY</name>
+ <desc>Interrupt priority, lower values are more prioritized.</desc>
+ <position>2</position>
+ <width>2</width>
+ </field>
+ <field>
+ <name>ARM_MODE</name>
+ <desc>Select between ARM's FIQ and IRQ mode</desc>
+ <position>4</position>
+ <width>1</width>
+ <enum>
+ <name>IRQ</name>
+ <value>0</value>
+ </enum>
+ <enum>
+ <name>FIQ</name>
+ <value>1</value>
+ </enum>
+ </field>
+</register>
+
+In this example, the 8-bit registers has three fields:
+- MODE(1:0): it has three named values DISABLED(0), ENABLED(1) and NMI(2)
+- PRIORITY(2:1): it has no named values
+- ARM_MODE(3): it has two named values IRQ(0) and FIQ(1)
+
+1.5) Register inheritance
+-------------------------
+
+The node hierarchy specifies instances, that is pairs of (path,address),
+and the register description describes the internal of a register. The placement
+of the register descriptions in the node hierarchy will specify which registers
+can be found at each address. More precisely, if a node contains a register
+description, it means that this node's and all sub-nodes' instances are registers
+following the description. It is forbidden for a node to contain a register
+description if one of its parents already contains one. The example below
+will make this concept clearer (for readability, we omitted some of the tags):
+
+<node>
+ <name>dma</name>
+ <instance><name>DMAC</name><address>0x80000000</address></instance>
+ <node>
+ <instance><name>PCM_CHAN</name><address>0x0</address></instance>
+ <instance><name>I2C_CHAN</name><address>0x10</address></instance>
+ <register><!--- blabla --></register>
+ <node>
+ <name>sct</name>
+ <instance><name>SET</name><address>0x4</address></instance>
+ <instance><name>CLR</name><address>0x8</address></instance>
+ <instance><name>TOG</name><address>0xC</address></instance>
+ </node>
+ </node>
+</node>
+
+This example describes one register (let's call it blabla) and 9 instances:
+- DMAC at 0x80000000, no register
+- DMAC.PCM_CHAN at 0x80000000, register blabla
+- DMAC.PCM_CHAN.SET at 0x80000004, register blabla
+- DMAC.PCM_CHAN.CLR at 0x80000008, register blabla
+- DMAC.PCM_CHAN.TOG at 0x8000000C, register blabla
+- DMAC.I2C_CHAN at 0x80000010, register blabla
+- DMAC.I2C_CHAN.SET at 0x80000014, register blabla
+- DMAC.I2C_CHAN.CLR at 0x80000018, register blabla
+- DMAC.I2C_CHAN.TOG at 0x8000001C, register blabla
+
+1.6) Soc description
+--------------------
+
+The description file must also specify some information about the system-on-chip
+itself. The entire description, including nodes, is contained in a "soc" tag
+which must at least specify the soc name. It can optionally specify the title
+and description, as well as the author(s) of the description, the ISA and
+the version.
+
+<soc>
+ <name>vsoc</name>
+ <title>Virtual SOC</title>
+ <desc>Virtual SoC is a nice and powerful chip.</desc>
+ <author>Amaury Pouly</author>
+ <isa>ARM</isa>
+ <version>0.5</version>
+ <!-- put nodes below -->
+</soc>
+
+2) Specification
+----------------
+
+Root
+----
+As any XML document, the content of the file should be enclosed in a "xml" tag.
+The root element must be "soc" tag.
+
+Example:
+<?xml version="1.0"?>
+<!-- desc -->
+</xml>
+
+Element: soc
+------------
+It must contain the following tags:
+- name: name of soc, only made of alphanumerical characters
+It can contain at most one of each of the following tags:
+- title: one line description of the soc
+- desc: free form description of the soc
+- isa: instruction set assembly
+- version: version of the description
+It can contain any number of the following tags:
+- author: author of the description
+- node: node description
+
+Element: node
+-------------
+It must contain the following tags:
+- name: name of node, only made of alphanumerical characters
+It can contain at most one of each of the following tags:
+- title: one line description of the node
+- desc: free form description of the node
+- register: register description
+It can contain any number of the following tags:
+- instance: author of the description
+- node: node description
+
+Element: instance
+-----------------
+It must contain the following tags:
+- name: name of instance, only made of alphanumerical characters
+It can contain at most one of each of the following tags:
+- title: one line description of the instance
+- desc: free form description of the instance
+- address: address for a single instance (non-negative number)
+- range: address range for multiple instances
+Note that address and range are mutually exclusive, and at least one of them
+must exists.
+
+Element: range
+--------------
+It must contain the following tags:
+- first: index of the first instance (non-negative number)
+- count: number of instances (positive number)
+It can contain at most one of each of the following tags:
+- base: base address (non-negative number)
+- stride: stride (number)
+- formula: free-form formula, must have a "variable" attribute
+Note that stride and formula are mutually exclusive, and at least one of them
+must exists. If stride is specified and base is omitted, it is taken to be 0.
+
+Element: register
+-----------------
+It can contain at most one of each of the following tags:
+- width: width in bits (positive number)
+It can contain any number of the following tags:
+- field: field description
+
+Element: field
+--------------
+It must contain the following tags:
+- name: name of field, only made of alphanumerical characters
+- position: least significant bit
+It can contain at most one of each of the following tags:
+- desc: free form description of the instance
+- width: width in bits
+It can contain any number of the following tags:
+- enum: enumerated value
+If the width is not specified, it is assumed to be 1.
+
+Element: enum
+-------------
+It must contain the following tags:
+- name: name of field, only made of alphanumerical characters
+- value: value (non-negative, must fit into the field's width)
+It can contain at most one of each of the following tags:
+- desc: free form description of the instance \ No newline at end of file
diff --git a/utils/regtools/headergen.cpp b/utils/regtools/headergen_v1.cpp
index 046e8b2fd8..7b38366d5d 100644
--- a/utils/regtools/headergen.cpp
+++ b/utils/regtools/headergen_v1.cpp
@@ -18,7 +18,7 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "soc_desc.hpp"
+#include "soc_desc_v1.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
@@ -28,6 +28,8 @@
#include <sys/types.h>
#include <getopt.h>
+using namespace soc_desc_v1;
+
#define HEADERGEN_VERSION "2.1.8"
#define error(...) do{ fprintf(stderr, __VA_ARGS__); exit(1); } while(0)
@@ -721,7 +723,7 @@ int main(int argc, char **argv)
for(int i = optind; i < argc - 1; i++)
{
soc_t s;
- if(!soc_desc_parse_xml(argv[i], s))
+ if(!parse_xml(argv[i], s))
{
printf("Cannot parse %s\n", argv[i]);
return 1;
diff --git a/utils/regtools/include/soc_desc.hpp b/utils/regtools/include/soc_desc.hpp
new file mode 100644
index 0000000000..66f2e3b6e1
--- /dev/null
+++ b/utils/regtools/include/soc_desc.hpp
@@ -0,0 +1,384 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 by Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __SOC_DESC__
+#define __SOC_DESC__
+
+#include <stdint.h>
+#include <vector>
+#include <list>
+#include <string>
+#include <map>
+
+namespace soc_desc
+{
+
+const size_t MAJOR_VERSION = 2;
+const size_t MINOR_VERSION = 0;
+const size_t REVISION_VERSION = 0;
+
+/** Typedef for SoC types: word, address and flags */
+typedef uint32_t soc_addr_t;
+typedef uint32_t soc_word_t;
+typedef int soc_id_t;
+
+/** Error class */
+class error_t
+{
+public:
+ enum level_t
+ {
+ INFO,
+ WARNING,
+ FATAL
+ };
+ error_t(level_t lvl, const std::string& loc, const std::string& msg)
+ :m_level(lvl), m_loc(loc), m_msg(msg) {}
+ level_t level() const { return m_level; }
+ std::string location() const { return m_loc; }
+ std::string message() const { return m_msg; }
+protected:
+ level_t m_level;
+ std::string m_loc, m_msg;
+};
+
+/** Error context to log errors */
+class error_context_t
+{
+public:
+ void add(const error_t& err) { m_list.push_back(err); }
+ size_t count() const { return m_list.size(); }
+ error_t get(size_t i) const { return m_list[i]; }
+protected:
+ std::vector< error_t > m_list;
+};
+
+/**
+ * Bare representation of the format
+ */
+
+/** Enumerated value (aka named value), represents a special value for a field */
+struct enum_t
+{
+ soc_id_t id; /** ID (must be unique among field enums) */
+ std::string name; /** Name (must be unique among field enums) */
+ std::string desc; /** Optional description of the meaning of this value */
+ soc_word_t value; /** Value of the field */
+};
+
+/** Register field information */
+struct field_t
+{
+ soc_id_t id; /** ID (must be unique among register fields) */
+ std::string name; /** Name (must be unique among register fields) */
+ std::string desc; /** Optional description of the field */
+ size_t pos; /** Position of the least significant bit */
+ size_t width; /** Width of the field in bits */
+ std::vector< enum_t > enum_; /** List of special values */
+
+ /** Returns the bit mask of the field within the register */
+ soc_word_t bitmask() const
+ {
+ // WARNING beware of the case where width is 32
+ if(width == 32)
+ return 0xffffffff;
+ else
+ return ((1 << width) - 1) << pos;
+ }
+
+ /** Extract field value from register value */
+ soc_word_t extract(soc_word_t reg_val) const
+ {
+ return (reg_val & bitmask()) >> pos;
+ }
+
+ /** Replace the field value in a register value */
+ soc_word_t replace(soc_word_t reg_val, soc_word_t field_val) const
+ {
+ return (reg_val & ~bitmask()) | ((field_val << pos) & bitmask());
+ }
+
+ /** Return field value index, or -1 if none */
+ int find_value(soc_word_t v) const
+ {
+ for(size_t i = 0; i < enum_.size(); i++)
+ if(enum_[i].value == v)
+ return i;
+ return -1;
+ }
+};
+
+/** Register information */
+struct register_t
+{
+ size_t width; /** Size in bits */
+ std::vector< field_t > field; /** List of fields */
+};
+
+/** Node address range information */
+struct range_t
+{
+ enum type_t
+ {
+ STRIDE, /** Addresses are given by a base address and a stride */
+ FORMULA /** Addresses are given by a formula */
+ };
+
+ type_t type; /** Range type */
+ size_t first; /** First index in the range */
+ size_t count; /** Number of indexes in the range */
+ soc_word_t base; /** Base address (for STRIDE) */
+ soc_word_t stride; /** Stride value (for STRIDE) */
+ std::string formula; /** Formula (for FORMULA) */
+ std::string variable; /** Formula variable name (for FORMULA) */
+};
+
+/** Node instance information */
+struct instance_t
+{
+ enum type_t
+ {
+ SINGLE, /** There is a single instance at a specified address */
+ RANGE /** There are multiple addresses forming a range */
+ };
+
+ soc_id_t id; /** ID (must be unique among node instances) */
+ std::string name; /** Name (must be unique among node instances) */
+ std::string title; /** Optional instance human name */
+ std::string desc; /** Optional description of the instance */
+ type_t type; /** Instance type */
+ soc_word_t addr; /** Address (for SINGLE) */
+ range_t range; /** Range (for RANGE) */
+};
+
+/** Node information */
+struct node_t
+{
+ soc_id_t id; /** ID (must be unique among nodes) */
+ std::string name; /** Name (must be unique for the among nodes) */
+ std::string title; /** Optional node human name */
+ std::string desc; /** Optional description of the node */
+ std::vector< register_t> register_; /** Optional register */
+ std::vector< instance_t> instance; /** List of instances */
+ std::vector< node_t > node; /** List of sub-nodes */
+};
+
+/** System-on-chip information */
+struct soc_t
+{
+ std::string name; /** Codename of the SoC */
+ std::string title; /** Human name of the SoC */
+ std::string desc; /** Optional description of the SoC */
+ std::string isa; /** Instruction Set Assembly */
+ std::string version; /** Description version */
+ std::vector< std::string > author; /** List of authors of the description */
+ std::vector< node_t > node; /** List of nodes */
+};
+
+/** Parse a SoC description from a XML file, put it into <soc>. */
+bool parse_xml(const std::string& filename, soc_t& soc, error_context_t& error_ctx);
+/** Write a SoC description to a XML file, overwriting it. A file can contain
+ * multiple Soc descriptions */
+bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& error_ctx);
+/** Formula parser: try to parse and evaluate a formula with some variables */
+bool evaluate_formula(const std::string& formula,
+ const std::map< std::string, soc_word_t>& var, soc_word_t& result,
+ const std::string& loc, error_context_t& error_ctx);
+
+/**
+ * Convenience API to manipulate the format
+ *
+ * The idea is that *_ref_t objects are stable pointers: they stay valid even
+ * when the underlying soc changes. In particular:
+ * - modifying any structure data (except id fields) preserves all references
+ * - removing a structure invalidates all references pointing to this structure
+ * and its children
+ * - adding any structure preserves all references
+ * These references can be used to get pointers to the actual data
+ * of the representation when it needs to be read or write.
+ */
+
+class soc_ref_t;
+class node_ref_t;
+class register_ref_t;
+class field_ref_t;
+class node_inst_t;
+
+/** SoC reference */
+class soc_ref_t
+{
+ soc_t *m_soc; /* pointer to the soc */
+public:
+ /** Builds an invalid reference */
+ soc_ref_t();
+ /** Builds a reference to a soc */
+ soc_ref_t(soc_t *soc);
+ /** Checks whether this reference is valid */
+ bool valid() const;
+ /** Returns a pointer to the soc */
+ soc_t *get() const;
+ /** Returns a reference to the root node */
+ node_ref_t root() const;
+ /** Returns a reference to the root node instance */
+ node_inst_t root_inst() const;
+ /** Compare this reference to another */
+ bool operator==(const soc_ref_t& r) const;
+ inline bool operator!=(const soc_ref_t& r) const { return !operator==(r); }
+};
+
+/** SoC node reference
+ * NOTE: the root soc node is presented as a node with empty path */
+class node_ref_t
+{
+ friend class soc_ref_t;
+ friend class node_inst_t;
+ soc_ref_t m_soc; /* reference to the soc */
+ std::vector< soc_id_t > m_path; /* path from the root */
+
+ node_ref_t(soc_ref_t soc);
+ node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path);
+public:
+ /** Builds an invalid reference */
+ node_ref_t();
+ /** Check whether this reference is valid */
+ bool valid() const;
+ /** Check whether this reference is the root node */
+ bool is_root() const;
+ /** Returns a pointer to the node, or 0 if invalid or root */
+ node_t *get() const;
+ /** Returns a reference to the soc */
+ soc_ref_t soc() const;
+ /** Returns a reference to the parent node */
+ node_ref_t parent() const;
+ /** Returns a reference to the register (which may be on a parent node) */
+ register_ref_t reg() const;
+ /** Returns a list of references to the sub-nodes */
+ std::vector< node_ref_t > children() const;
+ /** Returns a reference to a specific child */
+ node_ref_t child(const std::string& name) const;
+ /** Returns the path of the node, as the list of node names from the root */
+ std::vector< std::string > path() const;
+ /** Returns the name of the node */
+ std::string name() const;
+ /** Compare this reference to another */
+ bool operator==(const node_ref_t& r) const;
+ inline bool operator!=(const node_ref_t& r) const { return !operator==(r); }
+};
+
+/** SoC register reference */
+class register_ref_t
+{
+ friend class node_ref_t;
+ node_ref_t m_node; /* reference to the node owning the register */
+
+ register_ref_t(node_ref_t node);
+public:
+ /** Builds an invalid reference */
+ register_ref_t();
+ /** Check whether this reference is valid/exists */
+ bool valid() const;
+ /** Returns a pointer to the register, or 0 */
+ register_t *get() const;
+ /** Returns a reference to the node containing the register */
+ node_ref_t node() const;
+ /** Returns a list of references to the fields of the register */
+ std::vector< field_ref_t > fields() const;
+ /** Returns a reference to a particular field */
+ field_ref_t field(const std::string& name) const;
+ /** Compare this reference to another */
+ bool operator==(const register_ref_t& r) const;
+ inline bool operator!=(const register_ref_t& r) const { return !operator==(r); }
+};
+
+/** SoC register field reference */
+class field_ref_t
+{
+ friend class register_ref_t;
+ register_ref_t m_reg; /* reference to the register */
+ soc_id_t m_id; /* field name */
+
+ field_ref_t(register_ref_t reg, soc_id_t id);
+public:
+ /** Builds an invalid reference */
+ field_ref_t();
+ /** Check whether this reference is valid/exists */
+ bool valid() const;
+ /** Returns a pointer to the field, or 0 */
+ field_t *get() const;
+ /** Returns a reference to the register containing the field */
+ register_ref_t reg() const;
+ /** Compare this reference to another */
+ bool operator==(const field_ref_t& r) const;
+ inline bool operator!=(const field_ref_t& r) const { return !operator==(r); }
+};
+
+/** SoC node instance
+ * NOTE: the root soc node is presented as a node with a single instance at 0 */
+class node_inst_t
+{
+ friend class node_ref_t;
+ friend class soc_ref_t;
+ node_ref_t m_node; /* reference to the node */
+ std::vector< soc_id_t > m_id_path; /* list of instance IDs */
+ std::vector< size_t > m_index_path; /* list of instance indexes */
+
+ node_inst_t(soc_ref_t soc);
+ node_inst_t(node_ref_t soc, const std::vector< soc_id_t >& path,
+ const std::vector< size_t >& indexes);
+public:
+ /** Builds an invalid reference */
+ node_inst_t();
+ /** Check whether this instance is valid/exists */
+ bool valid() const;
+ /** Returns a reference to the soc */
+ soc_ref_t soc() const;
+ /** Returns a reference to the node */
+ node_ref_t node() const;
+ /** Check whether this reference is the root node instance */
+ bool is_root() const;
+ /** Returns a reference to the parent instance */
+ node_inst_t parent() const;
+ /** Returns a pointer to the instance of the node, or 0 */
+ instance_t *get() const;
+ /** Returns the address of this instance */
+ soc_addr_t addr() const;
+ /** Returns an instance to a child of this node's instance. If the subnode
+ * instance is a range, the returned reference is invalid */
+ node_inst_t child(const std::string& name) const;
+ /** Returns an instance to a child of this node's instance with a range index.
+ * If the subnode is not not a range or if the index is out of bounds,
+ * the returned reference is invalid */
+ node_inst_t child(const std::string& name, size_t index) const;
+ /** Returns a list of all instances of subnodes of this node's instance */
+ std::vector< node_inst_t > children() const;
+ /** Returns the name of the instance */
+ std::string name() const;
+ /** Checks whether this instance is indexed */
+ bool is_indexed() const;
+ /** Returns the index of the instance */
+ size_t index() const;
+ /** Compare this reference to another */
+ bool operator==(const node_inst_t& r) const;
+ inline bool operator!=(const node_inst_t& r) const { return !operator==(r); }
+};
+
+} // soc_desc
+
+#endif /* __SOC_DESC__ */
diff --git a/utils/regtools/lib/soc_desc.hpp b/utils/regtools/include/soc_desc_v1.hpp
index bb8eb4ba8d..33368e88d4 100644
--- a/utils/regtools/lib/soc_desc.hpp
+++ b/utils/regtools/include/soc_desc_v1.hpp
@@ -18,8 +18,8 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef __SOC_DESC__
-#define __SOC_DESC__
+#ifndef __SOC_DESC_V1__
+#define __SOC_DESC_V1__
#include <stdint.h>
#include <vector>
@@ -45,13 +45,12 @@
* ignores the position of the WORD_LENGTH field in the register.
*/
-#define SOCDESC_VERSION_MAJOR 1
-#define SOCDESC_VERSION_MINOR 4
-#define SOCDESC_VERSION_REV 1
+namespace soc_desc_v1
+{
-#define SOCDESC_VERSION__(maj,min,rev) #maj"."#min"."#rev
-#define SOCDESC_VERSION_(maj,min,rev) SOCDESC_VERSION__(maj,min,rev)
-#define SOCDESC_VERSION SOCDESC_VERSION_(SOCDESC_VERSION_MAJOR,SOCDESC_VERSION_MINOR,SOCDESC_VERSION_REV)
+const size_t MAJOR_VERSION = 1;
+const size_t MINOR_VERSION = 4;
+const size_t REVISION_VERSION = 1;
/**
* Typedef for SoC types: word, address and flags */
@@ -211,18 +210,21 @@ struct soc_t
};
/** Parse a SoC description from a XML file, append it to <soc>. */
-bool soc_desc_parse_xml(const std::string& filename, soc_t& soc);
+bool parse_xml(const std::string& filename, soc_t& soc);
/** Write a SoC description to a XML file, overwriting it. A file can contain
* multiple Soc descriptions */
-bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc);
+bool produce_xml(const std::string& filename, const soc_t& soc);
/** Normalise a soc description by reordering elemnts so that:
* - devices are sorted by first name
* - registers are sorted by first address
* - fields are sorted by last bit
* - values are sorted by value */
-void soc_desc_normalize(soc_t& soc);
-/** Formula parser: try to parse and evaluate a formula to a specific value of 'n' */
-bool soc_desc_evaluate_formula(const std::string& formula,
- const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error);
+void normalize(soc_t& soc);
+/** Formula parser: try to parse and evaluate a formula with some variables */
+bool evaluate_formula(const std::string& formula,
+ const std::map< std::string, soc_word_t>& var, soc_word_t& result,
+ std::string& error);
+
+} // soc_desc_v1
-#endif /* __SOC_DESC__ */ \ No newline at end of file
+#endif /* __SOC_DESC_V1__ */ \ No newline at end of file
diff --git a/utils/regtools/lib/Makefile b/utils/regtools/lib/Makefile
index 08021ef941..d7d6c1b07a 100644
--- a/utils/regtools/lib/Makefile
+++ b/utils/regtools/lib/Makefile
@@ -1,18 +1,15 @@
CC?=gcc
CXX?=g++
AR?=ar
-CFLAGS=-Wall -O2 `xml2-config --cflags` -std=c99 -g -fPIC
-CXXFLAGS=-Wall -O2 `xml2-config --cflags` -g -fPIC
+INCLUDE=../include/
+CXXFLAGS=-Wall -O2 `xml2-config --cflags` -g -fPIC -I$(INCLUDE)
LIB=libsocdesc.a
SRC=$(wildcard *.c)
SRCXX=$(wildcard *.cpp)
-OBJ=$(SRC:.c=.o) $(SRCXX:.cpp=.o)
+OBJ=$(SRCXX:.cpp=.o)
all: $(LIB) $(EXEC)
-%.o: %.c
- $(CC) $(CFLAGS) -c -o $@ $<
-
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
diff --git a/utils/regtools/lib/formula.cpp b/utils/regtools/lib/formula.cpp
new file mode 100644
index 0000000000..1a6b16c773
--- /dev/null
+++ b/utils/regtools/lib/formula.cpp
@@ -0,0 +1,214 @@
+#include "soc_desc.hpp"
+#include <cstdarg>
+#include <cstdio>
+
+using namespace soc_desc;
+
+namespace soc_desc
+{
+
+namespace
+{
+
+struct formula_evaluator
+{
+ std::string formula;
+ size_t pos;
+ error_context_t& ctx;
+ std::string m_loc;
+
+ bool err(const char *fmt, ...)
+ {
+ char buffer[256];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buffer,sizeof(buffer), fmt, args);
+ va_end(args);
+ ctx.add(error_t(error_t::FATAL, m_loc, buffer));
+ return false;
+ }
+
+ formula_evaluator(const std::string& s, error_context_t& ctx):pos(0),ctx(ctx)
+ {
+ for(size_t i = 0; i < s.size(); i++)
+ if(!isspace(s[i]))
+ formula.push_back(s[i]);
+ }
+
+ void set_location(const std::string& loc)
+ {
+ m_loc = loc;
+ }
+
+ void adv()
+ {
+ pos++;
+ }
+
+ char cur()
+ {
+ return end() ? 0 : formula[pos];
+ }
+
+ bool end()
+ {
+ return pos >= formula.size();
+ }
+
+ bool parse_digit(char c, int basis, soc_word_t& res)
+ {
+ c = tolower(c);
+ if(isdigit(c))
+ {
+ res = c - '0';
+ return true;
+ }
+ if(basis == 16 && isxdigit(c))
+ {
+ res = c + 10 - 'a';
+ return true;
+ }
+ return false;
+ }
+
+ bool parse_signed(soc_word_t& res)
+ {
+ char op = cur();
+ if(op == '+' || op == '-')
+ {
+ adv();
+ if(!parse_signed(res))
+ return false;
+ if(op == '-')
+ res *= -1;
+ return true;
+ }
+ else if(op == '(')
+ {
+ adv();
+ if(!parse_expression(res))
+ return false;
+ if(cur() != ')')
+ return err("expected ')', got '%c'", cur());
+ adv();
+ return true;
+ }
+ else if(isdigit(op))
+ {
+ res = op - '0';
+ adv();
+ int basis = 10;
+ if(op == '0' && cur() == 'x')
+ {
+ basis = 16;
+ adv();
+ }
+ soc_word_t digit = 0;
+ while(parse_digit(cur(), basis, digit))
+ {
+ res = res * basis + digit;
+ adv();
+ }
+ return true;
+ }
+ else if(isalpha(op) || op == '_')
+ {
+ std::string name;
+ while(isalnum(cur()) || cur() == '_')
+ {
+ name.push_back(cur());
+ adv();
+ }
+ return get_variable(name, res);
+ }
+ else
+ return err("express signed expression, got '%c'", op);
+ }
+
+ bool parse_term(soc_word_t& res)
+ {
+ if(!parse_signed(res))
+ return false;
+ while(cur() == '*' || cur() == '/' || cur() == '%')
+ {
+ char op = cur();
+ adv();
+ soc_word_t tmp;
+ if(!parse_signed(tmp))
+ return false;
+ if(op == '*')
+ res *= tmp;
+ else if(tmp != 0)
+ res = op == '/' ? res / tmp : res % tmp;
+ else
+ return err("division by 0");
+ }
+ return true;
+ }
+
+ bool parse_expression(soc_word_t& res)
+ {
+ if(!parse_term(res))
+ return false;
+ while(!end() && (cur() == '+' || cur() == '-'))
+ {
+ char op = cur();
+ adv();
+ soc_word_t tmp;
+ if(!parse_term(tmp))
+ return false;
+ if(op == '+')
+ res += tmp;
+ else
+ res -= tmp;
+ }
+ return true;
+ }
+
+ bool parse(soc_word_t& res)
+ {
+ bool ok = parse_expression(res);
+ if(ok && !end())
+ err("unexpected character '%c'", cur());
+ return ok && end();
+ }
+
+ virtual bool get_variable(std::string name, soc_word_t& res)
+ {
+ return err("unknown variable '%s'", name.c_str());
+ }
+};
+
+struct my_evaluator : public formula_evaluator
+{
+ const std::map< std::string, soc_word_t>& var;
+
+ my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var,
+ error_context_t& ctx)
+ :formula_evaluator(formula, ctx), var(_var) {}
+
+ virtual bool get_variable(std::string name, soc_word_t& res)
+ {
+ std::map< std::string, soc_word_t>::const_iterator it = var.find(name);
+ if(it == var.end())
+ return formula_evaluator::get_variable(name, res);
+ else
+ {
+ res = it->second;
+ return true;
+ }
+ }
+};
+
+}
+
+bool evaluate_formula(const std::string& formula,
+ const std::map< std::string, soc_word_t>& var, soc_word_t& result,
+ const std::string& loc, error_context_t& error)
+{
+ my_evaluator e(formula, var, error);
+ e.set_location(loc);
+ return e.parse(result);
+}
+
+} // soc_desc
diff --git a/utils/regtools/lib/soc_desc.cpp b/utils/regtools/lib/soc_desc.cpp
index 90e4e0eb81..4e0e46f00e 100644
--- a/utils/regtools/lib/soc_desc.cpp
+++ b/utils/regtools/lib/soc_desc.cpp
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2012 by Amaury Pouly
+ * Copyright (C) 2014 by Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -27,261 +27,429 @@
#include <string.h>
#include <algorithm>
#include <cctype>
+#include <sstream>
+#include <limits>
+
+namespace soc_desc
+{
+
+/**
+ * Parser
+ */
#define XML_CHAR_TO_CHAR(s) ((const char *)(s))
#define BEGIN_ATTR_MATCH(attr) \
for(xmlAttr *a = attr; a; a = a->next) {
-#define MATCH_X_ATTR(attr_name, hook, ...) \
+#define MATCH_UNIQUE_ATTR(attr_name, val, has, parse_fn, ctx) \
if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \
- std::string s; \
- if(!parse_text_attr(a, s) || !hook(s, __VA_ARGS__)) \
- return false; \
+ if(has) \
+ return parse_not_unique_attr_error(a, ctx); \
+ has = true; \
+ xmlChar *str = NULL; \
+ if(!parse_text_attr_internal(a, str, ctx) || !parse_fn(a, val, str, ctx)) \
+ ret = false; \
}
-#define SOFT_MATCH_X_ATTR(attr_name, hook, ...) \
- if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \
- std::string s; \
- if(parse_text_attr(a, s)) \
- hook(s, __VA_ARGS__); \
- }
-
-#define SOFT_MATCH_SCT_ATTR(attr_name, var) \
- SOFT_MATCH_X_ATTR(attr_name, validate_sct_hook, var)
-
-#define MATCH_TEXT_ATTR(attr_name, var) \
- MATCH_X_ATTR(attr_name, validate_string_hook, var)
-
-#define MATCH_UINT32_ATTR(attr_name, var) \
- MATCH_X_ATTR(attr_name, validate_uint32_hook, var)
-
-#define MATCH_BITRANGE_ATTR(attr_name, first, last) \
- MATCH_X_ATTR(attr_name, validate_bitrange_hook, first, last)
-
#define END_ATTR_MATCH() \
}
#define BEGIN_NODE_MATCH(node) \
for(xmlNode *sub = node; sub; sub = sub->next) {
-#define MATCH_ELEM_NODE(node_name, array, parse_fn) \
+#define MATCH_ELEM_NODE(node_name, array, parse_fn, ctx) \
if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
array.resize(array.size() + 1); \
- if(!parse_fn(sub, array.back())) \
- return false; \
+ if(!parse_fn(sub, array.back(), ctx)) \
+ ret = false; \
+ array.back().id = array.size(); \
}
-#define SOFT_MATCH_ELEM_NODE(node_name, array, parse_fn) \
+#define MATCH_TEXT_NODE(node_name, array, parse_fn, ctx) \
if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
+ if(!is_real_text_node(sub)) \
+ return parse_not_text_error(sub, ctx); \
+ xmlChar *content = xmlNodeGetContent(sub); \
array.resize(array.size() + 1); \
- if(!parse_fn(sub, array.back())) \
- array.pop_back(); \
+ ret = ret && parse_fn(sub, array.back(), content, ctx); \
+ xmlFree(content); \
+ }
+
+#define MATCH_UNIQUE_ELEM_NODE(node_name, val, has, parse_fn, ctx) \
+ if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
+ if(has) \
+ return parse_not_unique_error(sub, ctx); \
+ has = true; \
+ if(!parse_fn(sub, val, ctx)) \
+ ret = false; \
+ }
+
+#define MATCH_UNIQUE_TEXT_NODE(node_name, val, has, parse_fn, ctx) \
+ if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
+ if(has) \
+ return parse_not_unique_error(sub, ctx); \
+ if(!is_real_text_node(sub)) \
+ return parse_not_text_error(sub, ctx); \
+ has = true; \
+ xmlChar *content = xmlNodeGetContent(sub); \
+ ret = ret && parse_fn(sub, val, content, ctx); \
+ xmlFree(content); \
}
#define END_NODE_MATCH() \
}
+#define CHECK_HAS(node, node_name, has, ctx) \
+ if(!has) \
+ ret = ret && parse_missing_error(node, node_name, ctx);
+
+#define CHECK_HAS_ATTR(node, attr_name, has, ctx) \
+ if(!has) \
+ ret = ret && parse_missing_attr_error(node, attr_name, ctx);
+
namespace
{
-bool validate_string_hook(const std::string& str, std::string& s)
+bool is_real_text_node(xmlNode *node)
{
- s = str;
+ for(xmlNode *sub = node->children; sub; sub = sub->next)
+ if(sub->type != XML_TEXT_NODE && sub->type != XML_ENTITY_REF_NODE)
+ return false;
return true;
}
-bool validate_sct_hook(const std::string& str, soc_reg_flags_t& flags)
+std::string xml_loc(xmlNode *node)
{
- if(str == "yes") flags |= REG_HAS_SCT;
- else if(str != "no") return false;
- return true;
+ std::ostringstream oss;
+ oss << "line " << node->line;
+ return oss.str();
}
-bool validate_unsigned_long_hook(const std::string& str, unsigned long& s)
+std::string xml_loc(xmlAttr *attr)
{
- char *end;
- s = strtoul(str.c_str(), &end, 0);
- return *end == 0;
+ return xml_loc(attr->parent);
}
-bool validate_uint32_hook(const std::string& str, uint32_t& s)
+template<typename T>
+bool add_error(error_context_t& ctx, error_t::level_t lvl, T *node,
+ const std::string& msg)
{
- unsigned long u;
- if(!validate_unsigned_long_hook(str, u)) return false;
-#if ULONG_MAX > 0xffffffff
- if(u > 0xffffffff) return false;
-#endif
- s = u;
- return true;
+ ctx.add(error_t(lvl, xml_loc(node), msg));
+ return false;
}
-bool validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last)
+template<typename T>
+bool add_fatal(error_context_t& ctx, T *node, const std::string& msg)
{
- unsigned long a, b;
- size_t sep = str.find(':');
- if(sep == std::string::npos) return false;
- if(!validate_unsigned_long_hook(str.substr(0, sep), a)) return false;
- if(!validate_unsigned_long_hook(str.substr(sep + 1), b)) return false;
- if(a > 31 || b > 31 || a < b) return false;
- first = b;
- last = a;
- return true;
+ return add_error(ctx, error_t::FATAL, node, msg);
}
-bool parse_text_attr(xmlAttr *attr, std::string& s)
+template<typename T>
+bool add_warning(error_context_t& ctx, T *node, const std::string& msg)
{
- if(attr->children != attr->last)
- return false;
- if(attr->children->type != XML_TEXT_NODE)
- return false;
- s = XML_CHAR_TO_CHAR(attr->children->content);
- return true;
+ return add_error(ctx, error_t::WARNING, node, msg);
}
-bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value)
+bool parse_wrong_version_error(xmlNode *node, error_context_t& ctx)
{
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", value.name)
- MATCH_UINT32_ATTR("value", value.value)
- MATCH_TEXT_ATTR("desc", value.desc)
- END_ATTR_MATCH()
+ std::ostringstream oss;
+ oss << "unknown version, only version " << MAJOR_VERSION << " is supported";
+ return add_fatal(ctx, node, oss.str());
+}
- return true;
+bool parse_not_unique_error(xmlNode *node, error_context_t& ctx)
+{
+ std::ostringstream oss;
+ oss << "there must be a unique <" << XML_CHAR_TO_CHAR(node->name) << "> element";
+ if(node->parent->name)
+ oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">";
+ else
+ oss << " at root level";
+ return add_fatal(ctx, node, oss.str());
}
-bool parse_field_elem(xmlNode *node, soc_reg_field_t& field)
+bool parse_not_unique_attr_error(xmlAttr *attr, error_context_t& ctx)
{
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", field.name)
- MATCH_BITRANGE_ATTR("bitrange", field.first_bit, field.last_bit)
- MATCH_TEXT_ATTR("desc", field.desc)
- END_ATTR_MATCH()
+ std::ostringstream oss;
+ oss << "there must be a unique " << XML_CHAR_TO_CHAR(attr->name) << " attribute";
+ oss << " in <" << XML_CHAR_TO_CHAR(attr->parent->name) << ">";
+ return add_fatal(ctx, attr, oss.str());
+}
- BEGIN_NODE_MATCH(node->children)
- SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem)
- END_NODE_MATCH()
+bool parse_missing_error(xmlNode *node, const char *name, error_context_t& ctx)
+{
+ std::ostringstream oss;
+ oss << "missing <" << name << "> element";
+ if(node->parent->name)
+ oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">";
+ else
+ oss << " at root level";
+ return add_fatal(ctx, node, oss.str());
+}
- return true;
+bool parse_missing_attr_error(xmlNode *node, const char *name, error_context_t& ctx)
+{
+ std::ostringstream oss;
+ oss << "missing " << name << " attribute";
+ oss << " in <" << XML_CHAR_TO_CHAR(node->name) << ">";
+ return add_fatal(ctx, node, oss.str());
}
-bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr)
+bool parse_conflict_error(xmlNode *node, const char *name1, const char *name2,
+ error_context_t& ctx)
{
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", addr.name)
- MATCH_UINT32_ATTR("addr", addr.addr)
- END_ATTR_MATCH()
+ std::ostringstream oss;
+ oss << "conflicting <" << name1 << "> and <" << name2 << "> elements";
+ if(node->parent->name)
+ oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">";
+ else
+ oss << " at root level";
+ return add_fatal(ctx, node, oss.str());
+}
- return true;
+bool parse_not_text_error(xmlNode *node, error_context_t& ctx)
+{
+ return add_fatal(ctx, node, "this is not a text element");
}
-bool parse_reg_formula_elem(xmlNode *node, soc_reg_formula_t& formula)
+bool parse_not_text_attr_error(xmlAttr *attr, error_context_t& ctx)
{
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("string", formula.string)
- END_ATTR_MATCH()
+ return add_fatal(ctx, attr, "this is not a text attribute");
+}
- formula.type = REG_FORMULA_STRING;
+bool parse_text_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx)
+{
+ name = XML_CHAR_TO_CHAR(content);
+ return true;
+}
+bool parse_name_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx)
+{
+ name = XML_CHAR_TO_CHAR(content);
+ if(name.size() == 0)
+ return add_fatal(ctx, node, "name cannot be empty");
+ for(size_t i = 0; i < name.size(); i++)
+ if(!isalnum(name[i]) && name[i] != '_')
+ return add_fatal(ctx, node, "name must only contain alphanumeric characters or _");
return true;
}
-bool parse_add_trivial_addr(const std::string& str, soc_reg_t& reg)
+template<typename T, typename U>
+bool parse_unsigned_text(U *node, T& res, xmlChar *content, error_context_t& ctx)
{
- soc_reg_addr_t a;
- a.name = reg.name;
- if(!validate_uint32_hook(str, a.addr))
- return false;
- reg.addr.push_back(a);
+ char *end;
+ unsigned long uns = strtoul(XML_CHAR_TO_CHAR(content), &end, 0);
+ if(*end != 0)
+ return add_fatal(ctx, node, "content must be an unsigned integer");
+ res = uns;
+ if(res != uns)
+ return add_fatal(ctx, node, "value does not fit into allowed range");
return true;
}
-bool parse_reg_elem(xmlNode *node, soc_reg_t& reg)
+template<typename T>
+bool parse_unsigned_elem(xmlNode *node, T& res, xmlChar *content, error_context_t& ctx)
{
- std::list< soc_reg_formula_t > formulas;
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", reg.name)
- SOFT_MATCH_SCT_ATTR("sct", reg.flags)
- SOFT_MATCH_X_ATTR("addr", parse_add_trivial_addr, reg)
- MATCH_TEXT_ATTR("desc", reg.desc)
- END_ATTR_MATCH()
+ return parse_unsigned_text(node, res, content, ctx);
+}
- BEGIN_NODE_MATCH(node->children)
- MATCH_ELEM_NODE("addr", reg.addr, parse_reg_addr_elem)
- MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem)
- MATCH_ELEM_NODE("field", reg.field, parse_field_elem)
- END_NODE_MATCH()
+template<typename T>
+bool parse_unsigned_attr(xmlAttr *attr, T& res, xmlChar *content, error_context_t& ctx)
+{
+ return parse_unsigned_text(attr, res, content, ctx);
+}
- if(formulas.size() > 1)
- {
- fprintf(stderr, "Only one formula is allowed per register\n");
+bool parse_text_attr_internal(xmlAttr *attr, xmlChar*& res, error_context_t& ctx)
+{
+ if(attr->children != attr->last)
return false;
- }
- if(formulas.size() == 1)
- reg.formula = formulas.front();
-
+ if(attr->children->type != XML_TEXT_NODE)
+ return parse_not_text_attr_error(attr, ctx);
+ res = attr->children->content;
return true;
}
-bool parse_dev_addr_elem(xmlNode *node, soc_dev_addr_t& addr)
+bool parse_text_attr(xmlAttr *attr, std::string& res, xmlChar *content, error_context_t& ctx)
{
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", addr.name)
- MATCH_UINT32_ATTR("addr", addr.addr)
- END_ATTR_MATCH()
-
+ res = XML_CHAR_TO_CHAR(content);
return true;
}
-bool parse_dev_elem(xmlNode *node, soc_dev_t& dev)
+bool parse_enum_elem(xmlNode *node, enum_t& reg, error_context_t& ctx)
{
- BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", dev.name)
- MATCH_TEXT_ATTR("long_name", dev.long_name)
- MATCH_TEXT_ATTR("desc", dev.desc)
- MATCH_TEXT_ATTR("version", dev.version)
- END_ATTR_MATCH()
+ bool ret = true;
+ bool has_name = false, has_value = false, has_desc = false;
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_UNIQUE_TEXT_NODE("name", reg.name, has_name, parse_name_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("value", reg.value, has_value, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("desc", reg.desc, has_desc, parse_text_elem, ctx)
+ END_NODE_MATCH()
+ CHECK_HAS(node, "name", has_name, ctx)
+ CHECK_HAS(node, "value", has_value, ctx)
+ return ret;
+}
+bool parse_field_elem(xmlNode *node, field_t& field, error_context_t& ctx)
+{
+ bool ret = true;
+ bool has_name = false, has_pos = false, has_desc = false, has_width = false;
BEGIN_NODE_MATCH(node->children)
- MATCH_ELEM_NODE("addr", dev.addr, parse_dev_addr_elem)
- MATCH_ELEM_NODE("reg", dev.reg, parse_reg_elem)
+ MATCH_UNIQUE_TEXT_NODE("name", field.name, has_name, parse_name_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("position", field.pos, has_pos, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("width", field.width, has_width, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("desc", field.desc, has_desc, parse_text_elem, ctx)
+ MATCH_ELEM_NODE("enum", field.enum_, parse_enum_elem, ctx)
END_NODE_MATCH()
+ CHECK_HAS(node, "name", has_name, ctx)
+ CHECK_HAS(node, "position", has_pos, ctx)
+ if(!has_width)
+ field.width = 1;
+ return ret;
+}
- return true;
+bool parse_register_elem(xmlNode *node, register_t& reg, error_context_t& ctx)
+{
+ bool ret = true;
+ bool has_width = false;
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_UNIQUE_TEXT_NODE("width", reg.width, has_width, parse_unsigned_elem, ctx)
+ MATCH_ELEM_NODE("field", reg.field, parse_field_elem, ctx)
+ END_NODE_MATCH()
+ if(!has_width)
+ reg.width = 32;
+ return ret;
}
-bool parse_soc_elem(xmlNode *node, soc_t& soc)
+bool parse_formula_elem(xmlNode *node, range_t& range, error_context_t& ctx)
{
+ bool ret = true;
+ bool has_var = false;
BEGIN_ATTR_MATCH(node->properties)
- MATCH_TEXT_ATTR("name", soc.name)
- MATCH_TEXT_ATTR("desc", soc.desc)
- END_ATTR_MATCH()
+ MATCH_UNIQUE_ATTR("variable", range.variable, has_var, parse_text_attr, ctx)
+ END_NODE_MATCH()
+ CHECK_HAS_ATTR(node, "variable", has_var, ctx)
+ return ret;
+}
+bool parse_range_elem(xmlNode *node, range_t& range, error_context_t& ctx)
+{
+ bool ret = true;
+ bool has_first = false, has_count = false, has_stride = false, has_base = false;
+ bool has_formula = false, has_formula_attr = false;
BEGIN_NODE_MATCH(node->children)
- MATCH_ELEM_NODE("dev", soc.dev, parse_dev_elem)
+ MATCH_UNIQUE_TEXT_NODE("first", range.first, has_first, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("count", range.count, has_count, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("base", range.base, has_base, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("stride", range.stride, has_stride, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_ELEM_NODE("formula", range, has_formula_attr, parse_formula_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("formula", range.formula, has_formula, parse_text_elem, ctx)
END_NODE_MATCH()
+ CHECK_HAS(node, "first", has_first, ctx)
+ CHECK_HAS(node, "count", has_count, ctx)
+ if(!has_base && !has_formula)
+ ret = ret && parse_missing_error(node, "base> or <formula", ctx);
+ if(has_base && has_formula)
+ return parse_conflict_error(node, "base", "formula", ctx);
+ if(has_base)
+ CHECK_HAS(node, "stride", has_stride, ctx)
+ if(has_stride && !has_base)
+ ret = ret && parse_conflict_error(node, "stride", "formula", ctx);
+ if(has_stride)
+ range.type = range_t::STRIDE;
+ else
+ range.type = range_t::FORMULA;
+ return ret;
+}
- return true;
+bool parse_instance_elem(xmlNode *node, instance_t& inst, error_context_t& ctx)
+{
+ bool ret = true;
+ bool has_name = false, has_title = false, has_desc = false, has_range = false;
+ bool has_address = false;
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_UNIQUE_TEXT_NODE("name", inst.name, has_name, parse_name_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("title", inst.title, has_title, parse_text_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("desc", inst.desc, has_desc, parse_text_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("address", inst.addr, has_address, parse_unsigned_elem, ctx)
+ MATCH_UNIQUE_ELEM_NODE("range", inst.range, has_range, parse_range_elem, ctx)
+ END_NODE_MATCH()
+ CHECK_HAS(node, "name", has_name, ctx)
+ if(!has_address && !has_range)
+ ret = ret && parse_missing_error(node, "address> or <range", ctx);
+ if(has_address && has_range)
+ ret = ret && parse_conflict_error(node, "address", "range", ctx);
+ if(has_address)
+ inst.type = instance_t::SINGLE;
+ else
+ inst.type = instance_t::RANGE;
+ return ret;
}
-bool parse_root_elem(xmlNode *node, soc_t& soc)
+bool parse_node_elem(xmlNode *node_, node_t& node, error_context_t& ctx)
{
- std::vector< soc_t > socs;
- BEGIN_NODE_MATCH(node)
- MATCH_ELEM_NODE("soc", socs, parse_soc_elem)
+ bool ret = true;
+ register_t reg;
+ bool has_title = false, has_desc = false, has_register = false, has_name = false;
+ BEGIN_NODE_MATCH(node_->children)
+ MATCH_UNIQUE_TEXT_NODE("name", node.name, has_name, parse_name_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("title", node.title, has_title, parse_text_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("desc", node.desc, has_desc, parse_text_elem, ctx)
+ MATCH_UNIQUE_ELEM_NODE("register", reg, has_register, parse_register_elem, ctx)
+ MATCH_ELEM_NODE("node", node.node, parse_node_elem, ctx)
+ MATCH_ELEM_NODE("instance", node.instance, parse_instance_elem, ctx)
END_NODE_MATCH()
- if(socs.size() != 1)
+ CHECK_HAS(node_, "name", has_name, ctx)
+ CHECK_HAS(node_, "instance", !node.instance.empty(), ctx)
+ if(has_register)
+ node.register_.push_back(reg);
+ return ret;
+}
+
+bool parse_soc_elem(xmlNode *node, soc_t& soc, error_context_t& ctx)
+{
+ bool ret = true;
+ bool has_name = false, has_title = false, has_desc = false, has_version = false;
+ bool has_isa = false;
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_UNIQUE_TEXT_NODE("name", soc.name, has_name, parse_name_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("title", soc.title, has_title, parse_text_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("desc", soc.desc, has_desc, parse_text_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("version", soc.version, has_version, parse_text_elem, ctx)
+ MATCH_UNIQUE_TEXT_NODE("isa", soc.isa, has_isa, parse_text_elem, ctx)
+ MATCH_TEXT_NODE("author", soc.author, parse_text_elem, ctx)
+ MATCH_ELEM_NODE("node", soc.node, parse_node_elem, ctx)
+ END_NODE_MATCH()
+ CHECK_HAS(node, "name", has_name, ctx)
+ return ret;
+}
+
+bool parse_root_elem(xmlNode *node, soc_t& soc, error_context_t& ctx)
+{
+ size_t ver = 0;
+ bool ret = true;
+ bool has_soc = false, has_version = false;
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_UNIQUE_ATTR("version", ver, has_version, parse_unsigned_attr, ctx)
+ END_ATTR_MATCH()
+ if(!has_version)
{
- fprintf(stderr, "A description file must contain exactly one soc element\n");
+ ctx.add(error_t(error_t::FATAL, xml_loc(node), "no version attribute, is this a v1 file ?"));
return false;
}
- soc = socs[0];
- return true;
+ if(ver != MAJOR_VERSION)
+ return parse_wrong_version_error(node, ctx);
+ BEGIN_NODE_MATCH(node)
+ MATCH_UNIQUE_ELEM_NODE("soc", soc, has_soc, parse_soc_elem, ctx)
+ END_NODE_MATCH()
+ CHECK_HAS(node, "soc", has_soc, ctx)
+ return ret;
}
}
-bool soc_desc_parse_xml(const std::string& filename, soc_t& socs)
+bool parse_xml(const std::string& filename, soc_t& soc,
+ error_context_t& error_ctx)
{
LIBXML_TEST_VERSION
@@ -290,156 +458,207 @@ bool soc_desc_parse_xml(const std::string& filename, soc_t& socs)
return false;
xmlNodePtr root_element = xmlDocGetRootElement(doc);
- bool ret = parse_root_elem(root_element, socs);
+ bool ret = parse_root_elem(root_element, soc, error_ctx);
xmlFreeDoc(doc);
return ret;
}
+/**
+ * Producer
+ */
+
namespace
{
-int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field)
+#define SAFE(x) \
+ do{ \
+ if((x) < 0) { \
+ std::ostringstream oss; \
+ oss << __FILE__ << ":" << __LINE__; \
+ ctx.add(error_t(error_t::FATAL, oss.str(), "write error")); \
+ return -1; \
+ } \
+ }while(0)
+
+int produce_range(xmlTextWriterPtr writer, const range_t& range, error_context_t& ctx)
{
-#define SAFE(x) if((x) < 0) return -1;
- /* <field> */
- SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str()));
- /* desc */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str()));
- /* bitrange */
- SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d",
- field.last_bit, field.first_bit));
- /* values */
- for(size_t i = 0; i < field.value.size(); i++)
+ /* <range> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "range"));
+ /* <first/> */
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "first", "%lu", range.first));
+ /* <count/> */
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count));
+ /* <base/><stride/> */
+ if(range.type == range_t::STRIDE)
{
- /* <value> */
- SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str()));
- /* value */
- SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str()));
- /* </value> */
- SAFE(xmlTextWriterEndElement(writer));
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base));
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride));
}
- /* </field> */
- SAFE(xmlTextWriterEndElement(writer));
-#undef SAFE
- return 0;
-}
-
-int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg)
-{
-#define SAFE(x) if((x) < 0) return -1;
- /* <reg> */
- SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str()));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str()));
- /* flags */
- if(reg.flags & REG_HAS_SCT)
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes"));
- /* formula */
- if(reg.formula.type != REG_FORMULA_NONE)
+ /* <formula> */
+ else if(range.type == range_t::FORMULA)
{
/* <formula> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula"));
- switch(reg.formula.type)
- {
- case REG_FORMULA_STRING:
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string",
- BAD_CAST reg.formula.string.c_str()));
- break;
- default:
- break;
- }
+ /* variable */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "variable", BAD_CAST range.variable.c_str()));
+ /* content */
+ SAFE(xmlTextWriterWriteString(writer, BAD_CAST range.formula.c_str()));
/* </formula> */
SAFE(xmlTextWriterEndElement(writer));
}
- /* addresses */
- for(size_t i = 0; i < reg.addr.size(); i++)
- {
- /* <addr> */
- SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str()));
- /* addr */
- SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr));
- /* </addr> */
- SAFE(xmlTextWriterEndElement(writer));
- }
+ /* </range> */
+ SAFE(xmlTextWriterEndElement(writer));
+
+ return 0;
+}
+
+int produce_instance(xmlTextWriterPtr writer, const instance_t& inst, error_context_t& ctx)
+{
+ /* <instance> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "instance"));
+ /* <name/> */
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST inst.name.c_str()));
+ /* <title/> */
+ if(!inst.title.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST inst.title.c_str()));
+ /* <desc/> */
+ if(!inst.desc.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST inst.desc.c_str()));
+ /* <address/> */
+ if(inst.type == instance_t::SINGLE)
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", inst.addr));
+ /* <range/> */
+ else if(inst.type == instance_t::RANGE)
+ SAFE(produce_range(writer, inst.range, ctx));
+ /* </instance> */
+ SAFE(xmlTextWriterEndElement(writer));
+ return 0;
+}
+
+int produce_enum(xmlTextWriterPtr writer, const enum_t& enum_, error_context_t& ctx)
+{
+ /* <enum> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "enum"));
+ /* <name/> */
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST enum_.name.c_str()));
+ /* <desc/> */
+ if(!enum_.desc.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST enum_.desc.c_str()));
+ /* <value/> */
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "value", "0x%x", enum_.value));
+ /* </enum> */
+ SAFE(xmlTextWriterEndElement(writer));
+ return 0;
+}
+
+int produce_field(xmlTextWriterPtr writer, const field_t& field, error_context_t& ctx)
+{
+ /* <field> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field"));
+ /* <name/> */
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST field.name.c_str()));
+ /* <desc/> */
+ if(!field.desc.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str()));
+ /* <position/> */
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "position", "%lu", field.pos));
+ /* <width/> */
+ if(field.width != 1)
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", field.width));
+ /* enums */
+ for(size_t i = 0; i < field.enum_.size(); i++)
+ SAFE(produce_enum(writer, field.enum_[i], ctx));
+ /* </field> */
+ SAFE(xmlTextWriterEndElement(writer));
+ return 0;
+}
+
+int produce_register(xmlTextWriterPtr writer, const register_t& reg, error_context_t& ctx)
+{
+ /* <register> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "register"));
+ /* <width/> */
+ if(reg.width != 32)
+ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", reg.width));
/* fields */
for(size_t i = 0; i < reg.field.size(); i++)
- produce_field(writer, reg.field[i]);
- /* </reg> */
+ SAFE(produce_field(writer, reg.field[i], ctx));
+ /* </register> */
SAFE(xmlTextWriterEndElement(writer));
-#undef SAFE
return 0;
}
-int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev)
+int produce_node(xmlTextWriterPtr writer, const node_t& node, error_context_t& ctx)
{
-#define SAFE(x) if((x) < 0) return -1;
- /* <dev> */
- SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str()));
- /* long_name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str()));
- /* desc */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str()));
- /* version */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str()));
- /* addresses */
- for(size_t i = 0; i < dev.addr.size(); i++)
- {
- /* <addr> */
- SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str()));
- /* addr */
- SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr));
- /* </addr> */
- SAFE(xmlTextWriterEndElement(writer));
- }
- /* registers */
- for(size_t i = 0; i < dev.reg.size(); i++)
- produce_reg(writer, dev.reg[i]);
- /* </dev> */
+ /* <node> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "node"));
+ /* <name/> */
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST node.name.c_str()));
+ /* <title/> */
+ if(!node.title.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST node.title.c_str()));
+ /* <desc/> */
+ if(!node.desc.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST node.desc.c_str()));
+ /* instances */
+ for(size_t i = 0; i < node.instance.size(); i++)
+ SAFE(produce_instance(writer, node.instance[i], ctx));
+ /* register */
+ for(size_t i = 0; i < node.register_.size(); i++)
+ SAFE(produce_register(writer, node.register_[i], ctx));
+ /* nodes */
+ for(size_t i = 0; i < node.node.size(); i++)
+ SAFE(produce_node(writer, node.node[i], ctx));
+ /* </node> */
SAFE(xmlTextWriterEndElement(writer));
-#undef SAFE
return 0;
}
+#undef SAFE
+
}
-bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc)
+bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& ctx)
{
LIBXML_TEST_VERSION
+ std::ostringstream oss;
xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0);
if(writer == NULL)
return false;
-#define SAFE(x) if((x) < 0) goto Lerr
+#define SAFE(x) do{if((x) < 0) goto Lerr;}while(0)
SAFE(xmlTextWriterSetIndent(writer, 1));
- SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " "));
+ SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " "));
/* <xml> */
SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL));
/* <soc> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc"));
- /* name */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str()));
- /* desc */
- SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str()));
- /* devices */
- for(size_t i = 0; i < soc.dev.size(); i++)
- SAFE(produce_dev(writer, soc.dev[i]));
- /* end <soc> */
+ /* version */
+ oss << MAJOR_VERSION;
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST oss.str().c_str()));
+ /* <name/> */
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST soc.name.c_str()));
+ /* <title/> */
+ if(!soc.title.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST soc.title.c_str()));
+ /* <desc/> */
+ if(!soc.desc.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str()));
+ /* <author/> */
+ for(size_t i = 0; i < soc.author.size(); i++)
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "author", BAD_CAST soc.author[i].c_str()));
+ /* <isa/> */
+ if(!soc.isa.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "isa", BAD_CAST soc.isa.c_str()));
+ /* <version/> */
+ if(!soc.version.empty())
+ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "version", BAD_CAST soc.version.c_str()));
+ /* nodes */
+ for(size_t i = 0; i < soc.node.size(); i++)
+ SAFE(produce_node(writer, soc.node[i], ctx));
+ /* </soc> */
SAFE(xmlTextWriterEndElement(writer));
/* </xml> */
SAFE(xmlTextWriterEndDocument(writer));
@@ -451,520 +670,500 @@ Lerr:
return false;
}
-namespace
+/**
+ * soc_ref_t
+ */
+
+soc_ref_t::soc_ref_t():m_soc(0)
{
+}
-struct soc_sorter
+soc_ref_t::soc_ref_t(soc_t *soc):m_soc(soc)
{
- bool operator()(const soc_dev_t& a, const soc_dev_t& b) const
- {
- return a.name < b.name;
- }
+}
- bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const
- {
- return a.name < b.name;
- }
+bool soc_ref_t::valid() const
+{
+ return get() != 0;
+}
- bool operator()(const soc_reg_t& a, const soc_reg_t& b) const
- {
- soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0;
- soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0;
- return aa < ab;
- }
+soc_t *soc_ref_t::get() const
+{
+ return m_soc;
+}
- bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const
- {
- return a.addr < b.addr;
- }
+bool soc_ref_t::operator==(const soc_ref_t& ref) const
+{
+ return m_soc == ref.m_soc;
+}
- bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const
- {
- return a.last_bit > b.last_bit;
- }
+node_ref_t soc_ref_t::root() const
+{
+ return node_ref_t(*this);
+}
- bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const
- {
- return a.value < b.value;
- }
-};
+node_inst_t soc_ref_t::root_inst() const
+{
+ return node_inst_t(*this);
+}
-void normalize(soc_reg_field_t& field)
+/**
+ * node_ref_t */
+
+node_ref_t::node_ref_t(soc_ref_t soc):m_soc(soc)
{
- std::sort(field.value.begin(), field.value.end(), soc_sorter());
}
-void normalize(soc_reg_t& reg)
+node_ref_t::node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path)
+ :m_soc(soc), m_path(path)
{
- std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter());
- std::sort(reg.field.begin(), reg.field.end(), soc_sorter());
- for(size_t i = 0; i < reg.field.size(); i++)
- normalize(reg.field[i]);
}
-void normalize(soc_dev_t& dev)
+node_ref_t::node_ref_t()
{
- std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter());
- std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter());
- for(size_t i = 0; i < dev.reg.size(); i++)
- normalize(dev.reg[i]);
}
+bool node_ref_t::valid() const
+{
+ return (m_soc.valid() && is_root()) || get() != 0;
}
-void soc_desc_normalize(soc_t& soc)
+bool node_ref_t::is_root() const
{
- std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter());
- for(size_t i = 0; i < soc.dev.size(); i++)
- normalize(soc.dev[i]);
+ return m_path.empty();
}
namespace
{
- soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what)
- {
- soc_error_t err;
- err.level = lvl;
- err.location = at;
- err.message = what;
- return err;
- }
- soc_error_t make_warning(std::string at, std::string what)
- {
- return make_error(SOC_ERROR_WARNING, at, what);
- }
-
- soc_error_t make_fatal(std::string at, std::string what)
- {
- return make_error(SOC_ERROR_FATAL, at, what);
- }
+std::vector< node_t > *get_children(node_ref_t node)
+{
+ if(node.is_root())
+ return &node.soc().get()->node;
+ node_t *n = node.get();
+ return n == 0 ? 0 : &n->node;
+}
- soc_error_t prefix(soc_error_t err, const std::string& prefix_at)
- {
- err.location = prefix_at + "." + err.location;
- return err;
- }
+node_t *get_child(std::vector< node_t > *nodes, soc_id_t id)
+{
+ if(nodes == 0)
+ return 0;
+ for(size_t i = 0; i < nodes->size(); i++)
+ if((*nodes)[i].id == id)
+ return &(*nodes)[i];
+ return 0;
+}
- void add_errors(std::vector< soc_error_t >& errors,
- const std::vector< soc_error_t >& new_errors, const std::string& prefix_at)
- {
- for(size_t i = 0; i < new_errors.size(); i++)
- errors.push_back(prefix(new_errors[i], prefix_at));
- }
+node_t *get_child(std::vector< node_t > *nodes, const std::string& name)
+{
+ if(nodes == 0)
+ return 0;
+ for(size_t i = 0; i < nodes->size(); i++)
+ if((*nodes)[i].name == name)
+ return &(*nodes)[i];
+ return 0;
+}
- std::vector< soc_error_t > no_error()
- {
- std::vector< soc_error_t > s;
- return s;
- }
+}
- std::vector< soc_error_t > one_error(const soc_error_t& err)
+/* NOTE: valid() is implemented using get() != 0, so don't use it in get() ! */
+node_t *node_ref_t::get() const
+{
+ if(!soc().valid())
+ return 0;
+ /* we could do it recursively but it would make plenty of copies */
+ node_t *n = 0;
+ std::vector< node_t > *nodes = &soc().get()->node;
+ for(size_t i = 0; i < m_path.size(); i++)
{
- std::vector< soc_error_t > s;
- s.push_back(err);
- return s;
+ n = get_child(nodes, m_path[i]);
+ if(n == 0)
+ return 0;
+ nodes = &n->node;
}
+ return n;
+}
- bool name_valid(char c)
- {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') || c == '_';
- }
+soc_ref_t node_ref_t::soc() const
+{
+ return m_soc;
+}
- bool name_valid(const std::string& s)
- {
- for(size_t i = 0; i < s.size(); i++)
- if(!name_valid(s[i]))
- return false;
- return true;
- }
+node_ref_t node_ref_t::parent() const
+{
+ std::vector< soc_id_t > path = m_path;
+ if(!path.empty())
+ path.pop_back();
+ return node_ref_t(m_soc, path);
}
-std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive)
+register_ref_t node_ref_t::reg() const
{
- (void) recursive;
- if(name.size() == 0)
- return one_error(make_fatal(name, "empty name"));
- else if(!name_valid(name))
- return one_error(make_fatal(name, "invalid name"));
+ node_t *n = get();
+ if(n == 0)
+ return register_ref_t();
+ if(n->register_.empty())
+ return parent().reg();
else
- return no_error();
+ return register_ref_t(*this);
}
-std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive)
+node_ref_t node_ref_t::child(const std::string& name) const
{
- std::vector< soc_error_t > err;
- std::string at(name);
- if(name.size() == 0)
- err.push_back(make_fatal(at, "empty name"));
- else if(!name_valid(name))
- err.push_back(make_fatal(at, "invalid name"));
- if(last_bit > 31)
- err.push_back(make_fatal(at, "last bit is greater than 31"));
- if(first_bit > last_bit)
- err.push_back(make_fatal(at, "last bit is greater than first bit"));
- for(size_t i = 0; i < value.size(); i++)
+ /* check the node exists */
+ node_t *n = get_child(get_children(*this), name);
+ if(n == 0)
+ return node_ref_t();
+ std::vector< soc_id_t > path = m_path;
+ path.push_back(n->id);
+ return node_ref_t(m_soc, path);
+}
+
+std::vector< node_ref_t > node_ref_t::children() const
+{
+ std::vector< node_ref_t > nodes;
+ std::vector< node_t > *children = get_children(*this);
+ if(children == 0)
+ return nodes;
+ for(size_t i = 0; i < children->size(); i++)
{
- for(size_t j = 0; j < value.size(); j++)
+ std::vector< soc_id_t > path = m_path;
+ path.push_back((*children)[i].id);
+ nodes.push_back(node_ref_t(m_soc, path));
+ }
+ return nodes;
+}
+
+std::vector< std::string > node_ref_t::path() const
+{
+ std::vector< std::string > path;
+ if(!soc().valid())
+ return path;
+ /* we could do it recursively but this is more efficient */
+ node_t *n = 0;
+ std::vector< node_t > *nodes = &soc().get()->node;
+ for(size_t i = 0; i < m_path.size(); i++)
+ {
+ n = get_child(nodes, m_path[i]);
+ if(n == 0)
{
- if(i == j)
- continue;
- if(value[i].name == value[j].name)
- err.push_back(prefix(make_fatal(value[i].name,
- "there are several values with the same name"), at));
- if(value[i].value == value[j].value)
- err.push_back(prefix(make_warning(value[i].name,
- "there are several values with the same value"), at));
+ path.clear();
+ return path;
}
- if(value[i].value > (bitmask() >> first_bit))
- err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name));
- if(recursive)
- add_errors(err, value[i].errors(true), at);
+ path.push_back(n->name);
+ nodes = &n->node;
}
- return err;
+ return path;
}
-std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive)
+std::string node_ref_t::name() const
{
- (void) recursive;
- if(name.size() == 0)
- return one_error(make_fatal("", "empty name"));
- else if(!name_valid(name))
- return one_error(make_fatal(name, "invalid name"));
- else
- return no_error();
+ node_t *n = get();
+ return n == 0 ? "" : n->name;
}
-std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive)
+bool node_ref_t::operator==(const node_ref_t& ref) const
{
- (void) recursive;
- if(type == REG_FORMULA_STRING && string.size() == 0)
- return one_error(make_fatal("", "empty string formula"));
- else
- return no_error();
+ return m_soc == ref.m_soc && m_path == ref.m_path;
}
-namespace
+/**
+ * register_ref_t
+ */
+
+register_ref_t::register_ref_t(node_ref_t node)
+ :m_node(node)
{
+}
-bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b)
+register_ref_t::register_ref_t()
{
- return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit);
}
+bool register_ref_t::valid() const
+{
+ return get() != 0;
}
-std::vector< soc_error_t > soc_reg_t::errors(bool recursive)
+register_t *register_ref_t::get() const
{
- std::vector< soc_error_t > err;
- std::string at(name);
- if(name.size() == 0)
- err.push_back(make_fatal(at, "empty name"));
- else if(!name_valid(name))
- err.push_back(make_fatal(at, "invalid name"));
- for(size_t i = 0; i < addr.size(); i++)
- {
- for(size_t j = 0; j < addr.size(); j++)
- {
- if(i == j)
- continue;
- if(addr[i].name == addr[j].name)
- err.push_back(prefix(make_fatal(addr[i].name,
- "there are several instances with the same name"), at));
- if(addr[i].addr == addr[j].addr)
- err.push_back(prefix(make_fatal(addr[i].name,
- "there are several instances with the same address"), at));
- }
- if(recursive)
- add_errors(err, addr[i].errors(true), at);
- }
- if(recursive)
- add_errors(err, formula.errors(true), at);
- for(size_t i = 0; i < field.size(); i++)
- {
- for(size_t j = 0; j < field.size(); j++)
- {
- if(i == j)
- continue;
- if(field[i].name == field[j].name)
- err.push_back(prefix(make_fatal(field[i].name,
- "there are several fields with the same name"), at));
- if(field_overlap(field[i], field[j]))
- err.push_back(prefix(make_fatal(field[i].name,
- "there are overlapping fields"), at));
- }
- if(recursive)
- add_errors(err, field[i].errors(true), at);
- }
- return err;
+ node_t *n = m_node.get();
+ if(n == 0 || n->register_.empty())
+ return 0;
+ return &n->register_[0];
}
-std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive)
+node_ref_t register_ref_t::node() const
{
- (void) recursive;
- if(name.size() == 0)
- return one_error(make_fatal("", "empty name"));
- else if(!name_valid(name))
- return one_error(make_fatal(name, "invalid name"));
- else
- return no_error();
+ return m_node;
}
-std::vector< soc_error_t > soc_dev_t::errors(bool recursive)
+std::vector< field_ref_t > register_ref_t::fields() const
{
- std::vector< soc_error_t > err;
- std::string at(name);
- if(name.size() == 0)
- err.push_back(make_fatal(at, "empty name"));
- else if(!name_valid(name))
- err.push_back(make_fatal(at, "invalid name"));
- for(size_t i = 0; i < addr.size(); i++)
- {
- for(size_t j = 0; j < addr.size(); j++)
- {
- if(i == j)
- continue;
- if(addr[i].name == addr[j].name)
- err.push_back(prefix(make_fatal(addr[i].name,
- "there are several instances with the same name"), at));
- if(addr[i].addr == addr[j].addr)
- err.push_back(prefix(make_fatal(addr[i].name,
- "there are several instances with the same address"), at));
- }
- if(recursive)
- add_errors(err, addr[i].errors(true), at);
- }
- for(size_t i = 0; i < reg.size(); i++)
- {
- for(size_t j = 0; j < reg.size(); j++)
- {
- if(i == j)
- continue;
- if(reg[i].name == reg[j].name)
- err.push_back(prefix(make_fatal(reg[i].name,
- "there are several registers with the same name"), at));
- }
- if(recursive)
- add_errors(err, reg[i].errors(true), at);
- }
- return err;
+ std::vector< field_ref_t > fields;
+ register_t *r = get();
+ if(r == 0)
+ return fields;
+ for(size_t i = 0; i < r->field.size(); i++)
+ fields.push_back(field_ref_t(*this, r->field[i].id));
+ return fields;
}
-std::vector< soc_error_t > soc_t::errors(bool recursive)
+field_ref_t register_ref_t::field(const std::string& name) const
{
- std::vector< soc_error_t > err;
- std::string at(name);
- for(size_t i = 0; i < dev.size(); i++)
- {
- for(size_t j = 0; j < dev.size(); j++)
- {
- if(i == j)
- continue;
- if(dev[i].name == dev[j].name)
- err.push_back(prefix(make_fatal(dev[i].name,
- "there are several devices with the same name"), at));
- }
- if(recursive)
- add_errors(err, dev[i].errors(true), at);
- }
- return err;
+ register_t *r = get();
+ if(r == 0)
+ return field_ref_t();
+ for(size_t i = 0; i < r->field.size(); i++)
+ if(r->field[i].name == name)
+ return field_ref_t(*this, r->field[i].id);
+ return field_ref_t();
}
-namespace
+/**
+ * field_ref_t
+ */
+
+field_ref_t::field_ref_t(register_ref_t reg, soc_id_t id)
+ :m_reg(reg), m_id(id)
{
+}
-struct formula_evaluator
+field_ref_t::field_ref_t()
{
- std::string formula;
- size_t pos;
- std::string error;
+}
- bool err(const char *fmt, ...)
- {
- char buffer[256];
- va_list args;
- va_start(args, fmt);
- vsnprintf(buffer,sizeof(buffer), fmt, args);
- va_end(args);
- error = buffer;
- return false;
- }
+bool field_ref_t::valid() const
+{
+ return get() != 0;
+}
- formula_evaluator(const std::string& s):pos(0)
- {
- for(size_t i = 0; i < s.size(); i++)
- if(!isspace(s[i]))
- formula.push_back(s[i]);
- }
+field_t *field_ref_t::get() const
+{
+ register_t *reg = m_reg.get();
+ if(reg == 0)
+ return 0;
+ for(size_t i = 0; i < reg->field.size(); i++)
+ if(reg->field[i].id == m_id)
+ return &reg->field[i];
+ return 0;
+}
- void adv()
- {
- pos++;
- }
+register_ref_t field_ref_t::reg() const
+{
+ return m_reg;
+}
- char cur()
- {
- return end() ? 0 : formula[pos];
- }
+/**
+ * node_inst_t
+ */
- bool end()
- {
- return pos >= formula.size();
- }
+namespace
+{
+
+const size_t INST_NO_INDEX = std::numeric_limits<std::size_t>::max();
- bool parse_digit(char c, int basis, soc_word_t& res)
+bool get_inst_addr(range_t& range, size_t index, soc_addr_t& addr)
+{
+ if(index < range.first || index >= range.first + range.count)
+ return false;
+ switch(range.type)
{
- c = tolower(c);
- if(isdigit(c))
- {
- res = c - '0';
+ case range_t::STRIDE:
+ addr += range.base + (index - range.first) * range.stride;
return true;
- }
- if(basis == 16 && isxdigit(c))
+ case range_t::FORMULA:
{
- res = c + 10 - 'a';
+ soc_word_t res;
+ std::map< std::string, soc_word_t > vars;
+ vars[range.variable] = index;
+ error_context_t ctx;
+ if(!evaluate_formula(range.formula, vars, res, "", ctx))
+ return false;
+ addr += res;
return true;
}
- return err("invalid digit '%c'", c);
+ default:
+ return false;
}
+}
- bool parse_signed(soc_word_t& res)
+bool get_inst_addr(instance_t *inst, size_t index, soc_addr_t& addr)
+{
+ if(inst == 0)
+ return false;
+ switch(inst->type)
{
- char op = cur();
- if(op == '+' || op == '-')
- {
- adv();
- if(!parse_signed(res))
+ case instance_t::SINGLE:
+ if(index != INST_NO_INDEX)
return false;
- if(op == '-')
- res *= -1;
+ addr += inst->addr;
return true;
- }
- else if(op == '(')
- {
- adv();
- if(!parse_expression(res))
+ case instance_t::RANGE:
+ if(index == INST_NO_INDEX)
return false;
- if(cur() != ')')
- return err("expected ')', got '%c'", cur());
- adv();
- return true;
- }
- else if(isdigit(op))
- {
- res = op - '0';
- adv();
- int basis = 10;
- if(op == '0' && cur() == 'x')
- {
- basis = 16;
- adv();
- }
- soc_word_t digit = 0;
- while(parse_digit(cur(), basis, digit))
- {
- res = res * basis + digit;
- adv();
- }
- return true;
- }
- else if(isalpha(op) || op == '_')
- {
- std::string name;
- while(isalnum(cur()) || cur() == '_')
- {
- name.push_back(cur());
- adv();
- }
- return get_variable(name, res);
- }
- else
- return err("express signed expression, got '%c'", op);
- }
-
- bool parse_term(soc_word_t& res)
- {
- if(!parse_signed(res))
+ return get_inst_addr(inst->range, index, addr);
+ default:
return false;
- while(cur() == '*' || cur() == '/' || cur() == '%')
- {
- char op = cur();
- adv();
- soc_word_t tmp;
- if(!parse_signed(tmp))
- return false;
- if(op == '*')
- res *= tmp;
- else if(tmp != 0)
- res = op == '/' ? res / tmp : res % tmp;
- else
- return err("division by 0");
- }
- return true;
}
+}
- bool parse_expression(soc_word_t& res)
- {
- if(!parse_term(res))
- return false;
- while(!end() && (cur() == '+' || cur() == '-'))
- {
- char op = cur();
- adv();
- soc_word_t tmp;
- if(!parse_term(tmp))
- return false;
- if(op == '+')
- res += tmp;
- else
- res -= tmp;
- }
- return true;
- }
+}
- bool parse(soc_word_t& res, std::string& _error)
- {
- bool ok = parse_expression(res);
- if(ok && !end())
- err("unexpected character '%c'", cur());
- _error = error;
- return ok && end();
- }
+node_inst_t::node_inst_t(soc_ref_t soc)
+ :m_node(soc.root())
+{
+}
- virtual bool get_variable(std::string name, soc_word_t& res)
- {
- return err("unknown variable '%s'", name.c_str());
- }
-};
+node_inst_t::node_inst_t(node_ref_t node, const std::vector< soc_id_t >& ids,
+ const std::vector< size_t >& indexes)
+ :m_node(node), m_id_path(ids), m_index_path(indexes)
+{
+}
-struct my_evaluator : public formula_evaluator
+node_inst_t::node_inst_t()
{
- const std::map< std::string, soc_word_t>& var;
+}
- my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var)
- :formula_evaluator(formula), var(_var) {}
+bool node_inst_t::valid() const
+{
+ return is_root() || get() != 0;
+}
- virtual bool get_variable(std::string name, soc_word_t& res)
+node_ref_t node_inst_t::node() const
+{
+ return m_node;
+}
+
+soc_ref_t node_inst_t::soc() const
+{
+ return m_node.soc();
+}
+
+bool node_inst_t::is_root() const
+{
+ return m_node.is_root();
+}
+
+node_inst_t node_inst_t::parent() const
+{
+ std::vector< soc_id_t > ids = m_id_path;
+ std::vector< size_t > indexes = m_index_path;
+ if(!ids.empty())
+ ids.pop_back();
+ if(!indexes.empty())
+ indexes.pop_back();
+ return node_inst_t(m_node.parent(), ids, indexes);
+}
+
+instance_t *node_inst_t::get() const
+{
+ node_t *n = m_node.get();
+ if(n == 0)
+ return 0;
+ for(size_t i = 0; i < n->instance.size(); i++)
+ if(n->instance[i].id == m_id_path.back())
+ return &n->instance[i];
+ return 0;
+}
+
+soc_addr_t node_inst_t::addr() const
+{
+ if(is_root())
+ return 0;
+ soc_addr_t addr = parent().addr();
+ if(!get_inst_addr(get(), m_index_path.back(), addr))
+ return 0;
+ return addr;
+}
+
+node_inst_t node_inst_t::child(const std::string& name) const
+{
+ return child(name, INST_NO_INDEX);
+}
+
+
+node_inst_t node_inst_t::child(const std::string& name, size_t index) const
+{
+ std::vector< node_t > *nodes = get_children(m_node);
+ if(nodes == 0)
+ return node_inst_t();
+ node_ref_t child_node = m_node;
+ for(size_t i = 0; i < nodes->size(); i++)
{
- std::map< std::string, soc_word_t>::const_iterator it = var.find(name);
- if(it == var.end())
- return formula_evaluator::get_variable(name, res);
- else
+ node_t& node = (*nodes)[i];
+ child_node.m_path.push_back(node.id);
+ for(size_t j = 0; j < node.instance.size(); j++)
{
- res = it->second;
- return true;
+ if(node.instance[j].name != name)
+ continue;
+ std::vector< soc_id_t > ids = m_id_path;
+ std::vector< size_t > indexes = m_index_path;
+ ids.push_back(node.instance[j].id);
+ ids.push_back(index);
+ return node_inst_t(child_node, ids, indexes);
}
+ child_node.m_path.pop_back();
}
-};
+ return node_inst_t();
+}
+
+std::vector< node_inst_t > node_inst_t::children() const
+{
+ std::vector< node_inst_t > list;
+ std::vector< node_t > *nodes = get_children(m_node);
+ std::vector< soc_id_t > n_path = m_id_path;
+ std::vector< size_t > i_path = m_index_path;
+ if(nodes == 0)
+ return list;
+ node_ref_t child_node = m_node;
+ for(size_t i = 0; i < nodes->size(); i++)
+ {
+ node_t& node = (*nodes)[i];
+ child_node.m_path.push_back(node.id);
+ for(size_t j = 0; j < node.instance.size(); j++)
+ {
+ instance_t& inst = node.instance[j];
+ n_path.push_back(inst.id);
+ switch(inst.type)
+ {
+ case instance_t::SINGLE:
+ i_path.push_back(INST_NO_INDEX);
+ list.push_back(node_inst_t(child_node, n_path, i_path));
+ i_path.pop_back();
+ break;
+ case instance_t::RANGE:
+ for(size_t i = 0; i < inst.range.count; i++)
+ {
+ i_path.push_back(inst.range.first + i);
+ list.push_back(node_inst_t(child_node, n_path, i_path));
+ i_path.pop_back();
+ }
+ break;
+ default:
+ break;
+ }
+ n_path.pop_back();
+ }
+ child_node.m_path.pop_back();
+ }
+ return list;
+}
+
+std::string node_inst_t::name() const
+{
+ instance_t *inst = get();
+ return inst == 0 ? "" : inst->name;
+}
+bool node_inst_t:: is_indexed() const
+{
+ return !m_index_path.empty() && m_index_path.back() != INST_NO_INDEX;
}
-bool soc_desc_evaluate_formula(const std::string& formula,
- const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error)
+size_t node_inst_t::index() const
{
- my_evaluator e(formula, var);
- return e.parse(result, error);
+ return m_index_path.empty() ? INST_NO_INDEX : m_index_path.back();
}
/** WARNING we need to call xmlInitParser() to init libxml2 but it needs to
@@ -982,4 +1181,7 @@ public:
};
xml_parser_init __xml_parser_init;
-} \ No newline at end of file
+}
+
+} // soc_desc_v1
+
diff --git a/utils/regtools/lib/soc_desc_v1.cpp b/utils/regtools/lib/soc_desc_v1.cpp
new file mode 100644
index 0000000000..d585485493
--- /dev/null
+++ b/utils/regtools/lib/soc_desc_v1.cpp
@@ -0,0 +1,990 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2012 by Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "soc_desc_v1.hpp"
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlsave.h>
+#include <libxml/xmlwriter.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+#include <cctype>
+
+namespace soc_desc_v1
+{
+
+#define XML_CHAR_TO_CHAR(s) ((const char *)(s))
+
+#define BEGIN_ATTR_MATCH(attr) \
+ for(xmlAttr *a = attr; a; a = a->next) {
+
+#define MATCH_X_ATTR(attr_name, hook, ...) \
+ if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \
+ std::string s; \
+ if(!parse_text_attr(a, s) || !hook(s, __VA_ARGS__)) \
+ return false; \
+ }
+
+#define SOFT_MATCH_X_ATTR(attr_name, hook, ...) \
+ if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \
+ std::string s; \
+ if(parse_text_attr(a, s)) \
+ hook(s, __VA_ARGS__); \
+ }
+
+#define SOFT_MATCH_SCT_ATTR(attr_name, var) \
+ SOFT_MATCH_X_ATTR(attr_name, validate_sct_hook, var)
+
+#define MATCH_TEXT_ATTR(attr_name, var) \
+ MATCH_X_ATTR(attr_name, validate_string_hook, var)
+
+#define MATCH_UINT32_ATTR(attr_name, var) \
+ MATCH_X_ATTR(attr_name, validate_uint32_hook, var)
+
+#define MATCH_BITRANGE_ATTR(attr_name, first, last) \
+ MATCH_X_ATTR(attr_name, validate_bitrange_hook, first, last)
+
+#define END_ATTR_MATCH() \
+ }
+
+#define BEGIN_NODE_MATCH(node) \
+ for(xmlNode *sub = node; sub; sub = sub->next) {
+
+#define MATCH_ELEM_NODE(node_name, array, parse_fn) \
+ if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
+ array.resize(array.size() + 1); \
+ if(!parse_fn(sub, array.back())) \
+ return false; \
+ }
+
+#define SOFT_MATCH_ELEM_NODE(node_name, array, parse_fn) \
+ if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
+ array.resize(array.size() + 1); \
+ if(!parse_fn(sub, array.back())) \
+ array.pop_back(); \
+ }
+
+#define END_NODE_MATCH() \
+ }
+
+namespace
+{
+
+bool validate_string_hook(const std::string& str, std::string& s)
+{
+ s = str;
+ return true;
+}
+
+bool validate_sct_hook(const std::string& str, soc_reg_flags_t& flags)
+{
+ if(str == "yes") flags |= REG_HAS_SCT;
+ else if(str != "no") return false;
+ return true;
+}
+
+bool validate_unsigned_long_hook(const std::string& str, unsigned long& s)
+{
+ char *end;
+ s = strtoul(str.c_str(), &end, 0);
+ return *end == 0;
+}
+
+bool validate_uint32_hook(const std::string& str, uint32_t& s)
+{
+ unsigned long u;
+ if(!validate_unsigned_long_hook(str, u)) return false;
+#if ULONG_MAX > 0xffffffff
+ if(u > 0xffffffff) return false;
+#endif
+ s = u;
+ return true;
+}
+
+bool validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last)
+{
+ unsigned long a, b;
+ size_t sep = str.find(':');
+ if(sep == std::string::npos) return false;
+ if(!validate_unsigned_long_hook(str.substr(0, sep), a)) return false;
+ if(!validate_unsigned_long_hook(str.substr(sep + 1), b)) return false;
+ if(a > 31 || b > 31 || a < b) return false;
+ first = b;
+ last = a;
+ return true;
+}
+
+bool parse_text_attr(xmlAttr *attr, std::string& s)
+{
+ if(attr->children != attr->last)
+ return false;
+ if(attr->children->type != XML_TEXT_NODE)
+ return false;
+ s = XML_CHAR_TO_CHAR(attr->children->content);
+ return true;
+}
+
+bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", value.name)
+ MATCH_UINT32_ATTR("value", value.value)
+ MATCH_TEXT_ATTR("desc", value.desc)
+ END_ATTR_MATCH()
+
+ return true;
+}
+
+bool parse_field_elem(xmlNode *node, soc_reg_field_t& field)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", field.name)
+ MATCH_BITRANGE_ATTR("bitrange", field.first_bit, field.last_bit)
+ MATCH_TEXT_ATTR("desc", field.desc)
+ END_ATTR_MATCH()
+
+ BEGIN_NODE_MATCH(node->children)
+ SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem)
+ END_NODE_MATCH()
+
+ return true;
+}
+
+bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", addr.name)
+ MATCH_UINT32_ATTR("addr", addr.addr)
+ END_ATTR_MATCH()
+
+ return true;
+}
+
+bool parse_reg_formula_elem(xmlNode *node, soc_reg_formula_t& formula)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("string", formula.string)
+ END_ATTR_MATCH()
+
+ formula.type = REG_FORMULA_STRING;
+
+ return true;
+}
+
+bool parse_add_trivial_addr(const std::string& str, soc_reg_t& reg)
+{
+ soc_reg_addr_t a;
+ a.name = reg.name;
+ if(!validate_uint32_hook(str, a.addr))
+ return false;
+ reg.addr.push_back(a);
+ return true;
+}
+
+bool parse_reg_elem(xmlNode *node, soc_reg_t& reg)
+{
+ std::list< soc_reg_formula_t > formulas;
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", reg.name)
+ SOFT_MATCH_SCT_ATTR("sct", reg.flags)
+ SOFT_MATCH_X_ATTR("addr", parse_add_trivial_addr, reg)
+ MATCH_TEXT_ATTR("desc", reg.desc)
+ END_ATTR_MATCH()
+
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_ELEM_NODE("addr", reg.addr, parse_reg_addr_elem)
+ MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem)
+ MATCH_ELEM_NODE("field", reg.field, parse_field_elem)
+ END_NODE_MATCH()
+
+ if(formulas.size() > 1)
+ {
+ fprintf(stderr, "Only one formula is allowed per register\n");
+ return false;
+ }
+ if(formulas.size() == 1)
+ reg.formula = formulas.front();
+
+ return true;
+}
+
+bool parse_dev_addr_elem(xmlNode *node, soc_dev_addr_t& addr)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", addr.name)
+ MATCH_UINT32_ATTR("addr", addr.addr)
+ END_ATTR_MATCH()
+
+ return true;
+}
+
+bool parse_dev_elem(xmlNode *node, soc_dev_t& dev)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", dev.name)
+ MATCH_TEXT_ATTR("long_name", dev.long_name)
+ MATCH_TEXT_ATTR("desc", dev.desc)
+ MATCH_TEXT_ATTR("version", dev.version)
+ END_ATTR_MATCH()
+
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_ELEM_NODE("addr", dev.addr, parse_dev_addr_elem)
+ MATCH_ELEM_NODE("reg", dev.reg, parse_reg_elem)
+ END_NODE_MATCH()
+
+ return true;
+}
+
+bool parse_soc_elem(xmlNode *node, soc_t& soc)
+{
+ BEGIN_ATTR_MATCH(node->properties)
+ MATCH_TEXT_ATTR("name", soc.name)
+ MATCH_TEXT_ATTR("desc", soc.desc)
+ END_ATTR_MATCH()
+
+ BEGIN_NODE_MATCH(node->children)
+ MATCH_ELEM_NODE("dev", soc.dev, parse_dev_elem)
+ END_NODE_MATCH()
+
+ return true;
+}
+
+bool parse_root_elem(xmlNode *node, soc_t& soc)
+{
+ std::vector< soc_t > socs;
+ BEGIN_NODE_MATCH(node)
+ MATCH_ELEM_NODE("soc", socs, parse_soc_elem)
+ END_NODE_MATCH()
+ if(socs.size() != 1)
+ {
+ fprintf(stderr, "A description file must contain exactly one soc element\n");
+ return false;
+ }
+ soc = socs[0];
+ return true;
+}
+
+}
+
+bool parse_xml(const std::string& filename, soc_t& socs)
+{
+ LIBXML_TEST_VERSION
+
+ xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0);
+ if(doc == NULL)
+ return false;
+
+ xmlNodePtr root_element = xmlDocGetRootElement(doc);
+ bool ret = parse_root_elem(root_element, socs);
+
+ xmlFreeDoc(doc);
+
+ return ret;
+}
+
+namespace
+{
+
+int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field)
+{
+#define SAFE(x) if((x) < 0) return -1;
+ /* <field> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str()));
+ /* desc */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str()));
+ /* bitrange */
+ SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d",
+ field.last_bit, field.first_bit));
+ /* values */
+ for(size_t i = 0; i < field.value.size(); i++)
+ {
+ /* <value> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str()));
+ /* value */
+ SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str()));
+ /* </value> */
+ SAFE(xmlTextWriterEndElement(writer));
+ }
+ /* </field> */
+ SAFE(xmlTextWriterEndElement(writer));
+#undef SAFE
+ return 0;
+}
+
+int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg)
+{
+#define SAFE(x) if((x) < 0) return -1;
+ /* <reg> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str()));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str()));
+ /* flags */
+ if(reg.flags & REG_HAS_SCT)
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes"));
+ /* formula */
+ if(reg.formula.type != REG_FORMULA_NONE)
+ {
+ /* <formula> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula"));
+ switch(reg.formula.type)
+ {
+ case REG_FORMULA_STRING:
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string",
+ BAD_CAST reg.formula.string.c_str()));
+ break;
+ default:
+ break;
+ }
+ /* </formula> */
+ SAFE(xmlTextWriterEndElement(writer));
+ }
+ /* addresses */
+ for(size_t i = 0; i < reg.addr.size(); i++)
+ {
+ /* <addr> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str()));
+ /* addr */
+ SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr));
+ /* </addr> */
+ SAFE(xmlTextWriterEndElement(writer));
+ }
+ /* fields */
+ for(size_t i = 0; i < reg.field.size(); i++)
+ produce_field(writer, reg.field[i]);
+ /* </reg> */
+ SAFE(xmlTextWriterEndElement(writer));
+#undef SAFE
+ return 0;
+}
+
+int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev)
+{
+#define SAFE(x) if((x) < 0) return -1;
+ /* <dev> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str()));
+ /* long_name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str()));
+ /* desc */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str()));
+ /* version */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str()));
+ /* addresses */
+ for(size_t i = 0; i < dev.addr.size(); i++)
+ {
+ /* <addr> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str()));
+ /* addr */
+ SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr));
+ /* </addr> */
+ SAFE(xmlTextWriterEndElement(writer));
+ }
+ /* registers */
+ for(size_t i = 0; i < dev.reg.size(); i++)
+ produce_reg(writer, dev.reg[i]);
+ /* </dev> */
+ SAFE(xmlTextWriterEndElement(writer));
+#undef SAFE
+ return 0;
+}
+
+}
+
+bool produce_xml(const std::string& filename, const soc_t& soc)
+{
+ LIBXML_TEST_VERSION
+
+ xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0);
+ if(writer == NULL)
+ return false;
+#define SAFE(x) if((x) < 0) goto Lerr
+ SAFE(xmlTextWriterSetIndent(writer, 1));
+ SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " "));
+ /* <xml> */
+ SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL));
+ /* <soc> */
+ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc"));
+ /* name */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str()));
+ /* desc */
+ SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str()));
+ /* devices */
+ for(size_t i = 0; i < soc.dev.size(); i++)
+ SAFE(produce_dev(writer, soc.dev[i]));
+ /* end <soc> */
+ SAFE(xmlTextWriterEndElement(writer));
+ /* </xml> */
+ SAFE(xmlTextWriterEndDocument(writer));
+ xmlFreeTextWriter(writer);
+ return true;
+#undef SAFE
+Lerr:
+ xmlFreeTextWriter(writer);
+ return false;
+}
+
+namespace
+{
+
+struct soc_sorter
+{
+ bool operator()(const soc_dev_t& a, const soc_dev_t& b) const
+ {
+ return a.name < b.name;
+ }
+
+ bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const
+ {
+ return a.name < b.name;
+ }
+
+ bool operator()(const soc_reg_t& a, const soc_reg_t& b) const
+ {
+ soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0;
+ soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0;
+ return aa < ab;
+ }
+
+ bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const
+ {
+ return a.addr < b.addr;
+ }
+
+ bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const
+ {
+ return a.last_bit > b.last_bit;
+ }
+
+ bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const
+ {
+ return a.value < b.value;
+ }
+};
+
+void normalize(soc_reg_field_t& field)
+{
+ std::sort(field.value.begin(), field.value.end(), soc_sorter());
+}
+
+void normalize(soc_reg_t& reg)
+{
+ std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter());
+ std::sort(reg.field.begin(), reg.field.end(), soc_sorter());
+ for(size_t i = 0; i < reg.field.size(); i++)
+ normalize(reg.field[i]);
+}
+
+void normalize(soc_dev_t& dev)
+{
+ std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter());
+ std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter());
+ for(size_t i = 0; i < dev.reg.size(); i++)
+ normalize(dev.reg[i]);
+}
+
+}
+
+void normalize(soc_t& soc)
+{
+ std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter());
+ for(size_t i = 0; i < soc.dev.size(); i++)
+ normalize(soc.dev[i]);
+}
+
+namespace
+{
+ soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what)
+ {
+ soc_error_t err;
+ err.level = lvl;
+ err.location = at;
+ err.message = what;
+ return err;
+ }
+
+ soc_error_t make_warning(std::string at, std::string what)
+ {
+ return make_error(SOC_ERROR_WARNING, at, what);
+ }
+
+ soc_error_t make_fatal(std::string at, std::string what)
+ {
+ return make_error(SOC_ERROR_FATAL, at, what);
+ }
+
+ soc_error_t prefix(soc_error_t err, const std::string& prefix_at)
+ {
+ err.location = prefix_at + "." + err.location;
+ return err;
+ }
+
+ void add_errors(std::vector< soc_error_t >& errors,
+ const std::vector< soc_error_t >& new_errors, const std::string& prefix_at)
+ {
+ for(size_t i = 0; i < new_errors.size(); i++)
+ errors.push_back(prefix(new_errors[i], prefix_at));
+ }
+
+ std::vector< soc_error_t > no_error()
+ {
+ std::vector< soc_error_t > s;
+ return s;
+ }
+
+ std::vector< soc_error_t > one_error(const soc_error_t& err)
+ {
+ std::vector< soc_error_t > s;
+ s.push_back(err);
+ return s;
+ }
+
+ bool name_valid(char c)
+ {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || c == '_';
+ }
+
+ bool name_valid(const std::string& s)
+ {
+ for(size_t i = 0; i < s.size(); i++)
+ if(!name_valid(s[i]))
+ return false;
+ return true;
+ }
+}
+
+std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive)
+{
+ (void) recursive;
+ if(name.size() == 0)
+ return one_error(make_fatal(name, "empty name"));
+ else if(!name_valid(name))
+ return one_error(make_fatal(name, "invalid name"));
+ else
+ return no_error();
+}
+
+std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive)
+{
+ std::vector< soc_error_t > err;
+ std::string at(name);
+ if(name.size() == 0)
+ err.push_back(make_fatal(at, "empty name"));
+ else if(!name_valid(name))
+ err.push_back(make_fatal(at, "invalid name"));
+ if(last_bit > 31)
+ err.push_back(make_fatal(at, "last bit is greater than 31"));
+ if(first_bit > last_bit)
+ err.push_back(make_fatal(at, "last bit is greater than first bit"));
+ for(size_t i = 0; i < value.size(); i++)
+ {
+ for(size_t j = 0; j < value.size(); j++)
+ {
+ if(i == j)
+ continue;
+ if(value[i].name == value[j].name)
+ err.push_back(prefix(make_fatal(value[i].name,
+ "there are several values with the same name"), at));
+ if(value[i].value == value[j].value)
+ err.push_back(prefix(make_warning(value[i].name,
+ "there are several values with the same value"), at));
+ }
+ if(value[i].value > (bitmask() >> first_bit))
+ err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name));
+ if(recursive)
+ add_errors(err, value[i].errors(true), at);
+ }
+ return err;
+}
+
+std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive)
+{
+ (void) recursive;
+ if(name.size() == 0)
+ return one_error(make_fatal("", "empty name"));
+ else if(!name_valid(name))
+ return one_error(make_fatal(name, "invalid name"));
+ else
+ return no_error();
+}
+
+std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive)
+{
+ (void) recursive;
+ if(type == REG_FORMULA_STRING && string.size() == 0)
+ return one_error(make_fatal("", "empty string formula"));
+ else
+ return no_error();
+}
+
+namespace
+{
+
+bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b)
+{
+ return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit);
+}
+
+}
+
+std::vector< soc_error_t > soc_reg_t::errors(bool recursive)
+{
+ std::vector< soc_error_t > err;
+ std::string at(name);
+ if(name.size() == 0)
+ err.push_back(make_fatal(at, "empty name"));
+ else if(!name_valid(name))
+ err.push_back(make_fatal(at, "invalid name"));
+ for(size_t i = 0; i < addr.size(); i++)
+ {
+ for(size_t j = 0; j < addr.size(); j++)
+ {
+ if(i == j)
+ continue;
+ if(addr[i].name == addr[j].name)
+ err.push_back(prefix(make_fatal(addr[i].name,
+ "there are several instances with the same name"), at));
+ if(addr[i].addr == addr[j].addr)
+ err.push_back(prefix(make_fatal(addr[i].name,
+ "there are several instances with the same address"), at));
+ }
+ if(recursive)
+ add_errors(err, addr[i].errors(true), at);
+ }
+ if(recursive)
+ add_errors(err, formula.errors(true), at);
+ for(size_t i = 0; i < field.size(); i++)
+ {
+ for(size_t j = 0; j < field.size(); j++)
+ {
+ if(i == j)
+ continue;
+ if(field[i].name == field[j].name)
+ err.push_back(prefix(make_fatal(field[i].name,
+ "there are several fields with the same name"), at));
+ if(field_overlap(field[i], field[j]))
+ err.push_back(prefix(make_fatal(field[i].name,
+ "there are overlapping fields"), at));
+ }
+ if(recursive)
+ add_errors(err, field[i].errors(true), at);
+ }
+ return err;
+}
+
+std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive)
+{
+ (void) recursive;
+ if(name.size() == 0)
+ return one_error(make_fatal("", "empty name"));
+ else if(!name_valid(name))
+ return one_error(make_fatal(name, "invalid name"));
+ else
+ return no_error();
+}
+
+std::vector< soc_error_t > soc_dev_t::errors(bool recursive)
+{
+ std::vector< soc_error_t > err;
+ std::string at(name);
+ if(name.size() == 0)
+ err.push_back(make_fatal(at, "empty name"));
+ else if(!name_valid(name))
+ err.push_back(make_fatal(at, "invalid name"));
+ for(size_t i = 0; i < addr.size(); i++)
+ {
+ for(size_t j = 0; j < addr.size(); j++)
+ {
+ if(i == j)
+ continue;
+ if(addr[i].name == addr[j].name)
+ err.push_back(prefix(make_fatal(addr[i].name,
+ "there are several instances with the same name"), at));
+ if(addr[i].addr == addr[j].addr)
+ err.push_back(prefix(make_fatal(addr[i].name,
+ "there are several instances with the same address"), at));
+ }
+ if(recursive)
+ add_errors(err, addr[i].errors(true), at);
+ }
+ for(size_t i = 0; i < reg.size(); i++)
+ {
+ for(size_t j = 0; j < reg.size(); j++)
+ {
+ if(i == j)
+ continue;
+ if(reg[i].name == reg[j].name)
+ err.push_back(prefix(make_fatal(reg[i].name,
+ "there are several registers with the same name"), at));
+ }
+ if(recursive)
+ add_errors(err, reg[i].errors(true), at);
+ }
+ return err;
+}
+
+std::vector< soc_error_t > soc_t::errors(bool recursive)
+{
+ std::vector< soc_error_t > err;
+ std::string at(name);
+ for(size_t i = 0; i < dev.size(); i++)
+ {
+ for(size_t j = 0; j < dev.size(); j++)
+ {
+ if(i == j)
+ continue;
+ if(dev[i].name == dev[j].name)
+ err.push_back(prefix(make_fatal(dev[i].name,
+ "there are several devices with the same name"), at));
+ }
+ if(recursive)
+ add_errors(err, dev[i].errors(true), at);
+ }
+ return err;
+}
+
+namespace
+{
+
+struct formula_evaluator
+{
+ std::string formula;
+ size_t pos;
+ std::string error;
+
+ bool err(const char *fmt, ...)
+ {
+ char buffer[256];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buffer,sizeof(buffer), fmt, args);
+ va_end(args);
+ error = buffer;
+ return false;
+ }
+
+ formula_evaluator(const std::string& s):pos(0)
+ {
+ for(size_t i = 0; i < s.size(); i++)
+ if(!isspace(s[i]))
+ formula.push_back(s[i]);
+ }
+
+ void adv()
+ {
+ pos++;
+ }
+
+ char cur()
+ {
+ return end() ? 0 : formula[pos];
+ }
+
+ bool end()
+ {
+ return pos >= formula.size();
+ }
+
+ bool parse_digit(char c, int basis, soc_word_t& res)
+ {
+ c = tolower(c);
+ if(isdigit(c))
+ {
+ res = c - '0';
+ return true;
+ }
+ if(basis == 16 && isxdigit(c))
+ {
+ res = c + 10 - 'a';
+ return true;
+ }
+ return err("invalid digit '%c'", c);
+ }
+
+ bool parse_signed(soc_word_t& res)
+ {
+ char op = cur();
+ if(op == '+' || op == '-')
+ {
+ adv();
+ if(!parse_signed(res))
+ return false;
+ if(op == '-')
+ res *= -1;
+ return true;
+ }
+ else if(op == '(')
+ {
+ adv();
+ if(!parse_expression(res))
+ return false;
+ if(cur() != ')')
+ return err("expected ')', got '%c'", cur());
+ adv();
+ return true;
+ }
+ else if(isdigit(op))
+ {
+ res = op - '0';
+ adv();
+ int basis = 10;
+ if(op == '0' && cur() == 'x')
+ {
+ basis = 16;
+ adv();
+ }
+ soc_word_t digit = 0;
+ while(parse_digit(cur(), basis, digit))
+ {
+ res = res * basis + digit;
+ adv();
+ }
+ return true;
+ }
+ else if(isalpha(op) || op == '_')
+ {
+ std::string name;
+ while(isalnum(cur()) || cur() == '_')
+ {
+ name.push_back(cur());
+ adv();
+ }
+ return get_variable(name, res);
+ }
+ else
+ return err("express signed expression, got '%c'", op);
+ }
+
+ bool parse_term(soc_word_t& res)
+ {
+ if(!parse_signed(res))
+ return false;
+ while(cur() == '*' || cur() == '/' || cur() == '%')
+ {
+ char op = cur();
+ adv();
+ soc_word_t tmp;
+ if(!parse_signed(tmp))
+ return false;
+ if(op == '*')
+ res *= tmp;
+ else if(tmp != 0)
+ res = op == '/' ? res / tmp : res % tmp;
+ else
+ return err("division by 0");
+ }
+ return true;
+ }
+
+ bool parse_expression(soc_word_t& res)
+ {
+ if(!parse_term(res))
+ return false;
+ while(!end() && (cur() == '+' || cur() == '-'))
+ {
+ char op = cur();
+ adv();
+ soc_word_t tmp;
+ if(!parse_term(tmp))
+ return false;
+ if(op == '+')
+ res += tmp;
+ else
+ res -= tmp;
+ }
+ return true;
+ }
+
+ bool parse(soc_word_t& res, std::string& _error)
+ {
+ bool ok = parse_expression(res);
+ if(ok && !end())
+ err("unexpected character '%c'", cur());
+ _error = error;
+ return ok && end();
+ }
+
+ virtual bool get_variable(std::string name, soc_word_t& res)
+ {
+ return err("unknown variable '%s'", name.c_str());
+ }
+};
+
+struct my_evaluator : public formula_evaluator
+{
+ const std::map< std::string, soc_word_t>& var;
+
+ my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var)
+ :formula_evaluator(formula), var(_var) {}
+
+ virtual bool get_variable(std::string name, soc_word_t& res)
+ {
+ std::map< std::string, soc_word_t>::const_iterator it = var.find(name);
+ if(it == var.end())
+ return formula_evaluator::get_variable(name, res);
+ else
+ {
+ res = it->second;
+ return true;
+ }
+ }
+};
+
+}
+
+bool evaluate_formula(const std::string& formula,
+ const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error)
+{
+ my_evaluator e(formula, var);
+ return e.parse(result, error);
+}
+
+/** WARNING we need to call xmlInitParser() to init libxml2 but it needs to
+ * called from the main thread, which is a super strong requirement, so do it
+ * using a static constructor */
+namespace
+{
+class xml_parser_init
+{
+public:
+ xml_parser_init()
+ {
+ xmlInitParser();
+ }
+};
+
+xml_parser_init __xml_parser_init;
+}
+
+} // soc_desc_v1
diff --git a/utils/regtools/qeditor/backend.cpp b/utils/regtools/qeditor/backend.cpp
index 7091a0c77f..cbaff7370d 100644
--- a/utils/regtools/qeditor/backend.cpp
+++ b/utils/regtools/qeditor/backend.cpp
@@ -35,8 +35,8 @@ SocFile::SocFile()
SocFile::SocFile(const QString& filename)
:m_filename(filename)
{
- m_valid = soc_desc_parse_xml(filename.toStdString(), m_soc);
- soc_desc_normalize(m_soc);
+ m_valid = parse_xml(filename.toStdString(), m_soc);
+ normalize(m_soc);
}
bool SocFile::IsValid()
diff --git a/utils/regtools/qeditor/backend.h b/utils/regtools/qeditor/backend.h
index b2f53c9ed5..934c1c359b 100644
--- a/utils/regtools/qeditor/backend.h
+++ b/utils/regtools/qeditor/backend.h
@@ -29,7 +29,11 @@
#ifdef HAVE_HWSTUB
#include "hwstub.h"
#endif
-#include "soc_desc.hpp"
+#include "soc_desc_v1.hpp"
+
+/* we don't want to import the entire soc_desc except for a few selected
+ * pieces */
+using namespace soc_desc_v1;
class IoBackend : public QObject
{
diff --git a/utils/regtools/qeditor/mainwindow.cpp b/utils/regtools/qeditor/mainwindow.cpp
index 9cea728d6e..b44306d701 100644
--- a/utils/regtools/qeditor/mainwindow.cpp
+++ b/utils/regtools/qeditor/mainwindow.cpp
@@ -161,12 +161,14 @@ void MainWindow::OnQuit()
void MainWindow::OnAbout()
{
+ QString soc_desc_ver = QString("%1.%2.%3").arg(MAJOR_VERSION)
+ .arg(MINOR_VERSION).arg(REVISION_VERSION);
QMessageBox::about(this, "About",
"<h1>QEditor</h1>"
"<h2>Version "APP_VERSION"</h2>"
"<p>Written by Amaury Pouly</p>"
"<p>Libraries:</p>"
- "<ul><li>soc_desc: "SOCDESC_VERSION"</li>"
+ "<ul><li>soc_desc: " + soc_desc_ver + "</li>"
#ifdef HAVE_HWSTUB
"<li>hwstub: "HWSTUB_VERSION"</li>"
#else
diff --git a/utils/regtools/qeditor/qeditor.pro b/utils/regtools/qeditor/qeditor.pro
index eecc0aba3f..265cc93171 100644
--- a/utils/regtools/qeditor/qeditor.pro
+++ b/utils/regtools/qeditor/qeditor.pro
@@ -5,14 +5,14 @@ HEADERS += mainwindow.h backend.h regtab.h analyser.h settings.h \
SOURCES += main.cpp mainwindow.cpp regtab.cpp backend.cpp analyser.cpp \
std_analysers.cpp settings.cpp utils.cpp regdisplaypanel.cpp regedit.cpp
LIBS += -L../lib/ -lsocdesc -lxml2
-INCLUDEPATH += ../lib/ ../../hwstub/lib
+INCLUDEPATH += ../include/ ../../hwstub/lib
DEPENDPATH += ../
libsocdesc.commands = cd ../lib && make
QMAKE_EXTRA_TARGETS += libsocdesc
PRE_TARGETDEPS += libsocdesc
-VERSION = 2.0.4
+VERSION = 2.1.0
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
diff --git a/utils/regtools/qeditor/regedit.cpp b/utils/regtools/qeditor/regedit.cpp
index 851e054eb0..8b4bfb7c49 100644
--- a/utils/regtools/qeditor/regedit.cpp
+++ b/utils/regtools/qeditor/regedit.cpp
@@ -421,7 +421,7 @@ void RegEditPanel::OnFormulaGenerate(bool checked)
map["n"] = n;
std::string err;
soc_word_t res;
- if(!soc_desc_evaluate_formula(formula, map, res, err))
+ if(!evaluate_formula(formula, map, res, err))
{
qDebug() << "Cannot evaluator " << QString::fromStdString(formula)
<< "for n=" << n << ": " << QString::fromStdString(err);
@@ -1051,8 +1051,8 @@ void RegEdit::OnNew()
bool RegEdit::SaveSocFile(const QString& filename)
{
- soc_desc_normalize(m_cur_socfile.GetSoc());
- if(!soc_desc_produce_xml(filename.toStdString(), m_cur_socfile.GetSoc()))
+ normalize(m_cur_socfile.GetSoc());
+ if(!produce_xml(filename.toStdString(), m_cur_socfile.GetSoc()))
{
QMessageBox::warning(this, "The description was not saved",
"There was an error when saving the file");
diff --git a/utils/regtools/qeditor/utils.cpp b/utils/regtools/qeditor/utils.cpp
index e4a872ed49..2ee1bc6db5 100644
--- a/utils/regtools/qeditor/utils.cpp
+++ b/utils/regtools/qeditor/utils.cpp
@@ -1004,7 +1004,7 @@ void MyTextEditor::SetReadOnly(bool en)
if(en)
m_toolbar->hide();
else
- m_toolbar->hide();
+ m_toolbar->show();
m_edit->setReadOnly(en);
}
diff --git a/utils/regtools/swiss_knife.cpp b/utils/regtools/swiss_knife.cpp
new file mode 100644
index 0000000000..eaa2519a27
--- /dev/null
+++ b/utils/regtools/swiss_knife.cpp
@@ -0,0 +1,612 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 by Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "soc_desc.hpp"
+#include "soc_desc_v1.hpp"
+#include <stdio.h>
+#include <stdlib.h>
+#include <map>
+#include <set>
+#include <cstring>
+
+using namespace soc_desc;
+
+void print_context(const error_context_t& ctx)
+{
+ for(size_t j = 0; j < ctx.count(); j++)
+ {
+ error_t e = ctx.get(j);
+ switch(e.level())
+ {
+ case error_t::INFO: printf("[INFO]"); break;
+ case error_t::WARNING: printf("[WARN]"); break;
+ case error_t::FATAL: printf("[FATAL]"); break;
+ default: printf("[UNK]"); break;
+ }
+ if(e.location().size() != 0)
+ printf(" %s:", e.location().c_str());
+ printf(" %s\n", e.message().c_str());
+ }
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_reg_field_value_t& in, enum_t& out, error_context_t& ctx)
+{
+ out.name = in.name;
+ out.desc = in.desc;
+ out.value = in.value;
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_reg_field_t& in, field_t& out, error_context_t& ctx)
+{
+ out.name = in.name;
+ out.desc = in.desc;
+ out.pos = in.first_bit;
+ out.width = in.last_bit - in.first_bit + 1;
+ out.enum_.resize(in.value.size());
+ for(size_t i = 0; i < in.value.size(); i++)
+ if(!convert_v1_to_v2(in.value[i], out.enum_[i], ctx))
+ return false;
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_reg_addr_t& in, instance_t& out, error_context_t& ctx)
+{
+ out.name = in.name;
+ out.type = instance_t::SINGLE;
+ out.addr = in.addr;
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_reg_formula_t& in, range_t& out, error_context_t& ctx)
+{
+ out.type = range_t::FORMULA;
+ out.formula = in.string;
+ out.variable = "n";
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_reg_t& in, node_t& out, error_context_t& ctx,
+ std::string _loc)
+{
+ std::string loc = _loc + "." + in.name;
+ out.name = in.name;
+ out.desc = in.desc;
+ if(in.formula.type == soc_desc_v1::REG_FORMULA_NONE)
+ {
+ out.instance.resize(in.addr.size());
+ for(size_t i = 0; i < in.addr.size(); i++)
+ if(!convert_v1_to_v2(in.addr[i], out.instance[i], ctx))
+ return false;
+ }
+ else
+ {
+ out.instance.resize(1);
+ out.instance[0].name = in.name;
+ out.instance[0].type = instance_t::RANGE;
+ out.instance[0].range.first = 0;
+ out.instance[0].range.count = in.addr.size();
+ /* check if formula is base/stride */
+ bool is_stride = true;
+ soc_word_t base = 0, stride = 0;
+ if(in.addr.size() <= 1)
+ {
+ ctx.add(error_t(error_t::WARNING, loc,
+ "register uses a formula but has only one instance"));
+ is_stride = false;
+ }
+ else
+ {
+ base = in.addr[0].addr;
+ stride = in.addr[1].addr - base;
+ for(size_t i = 0; i < in.addr.size(); i++)
+ if(base + i * stride != in.addr[i].addr)
+ is_stride = false;
+ }
+
+ if(is_stride)
+ {
+ ctx.add(error_t(error_t::INFO, loc, "promoted formula to base/stride"));
+ out.instance[0].range.type = range_t::STRIDE;
+ out.instance[0].range.base = base;
+ out.instance[0].range.stride = stride;
+ }
+ else if(!convert_v1_to_v2(in.formula, out.instance[0].range, ctx))
+ return false;
+ }
+ out.register_.resize(1);
+ out.register_[0].width = 32;
+ out.register_[0].field.resize(in.field.size());
+ for(size_t i = 0; i < in.field.size(); i++)
+ if(!convert_v1_to_v2(in.field[i], out.register_[0].field[i], ctx))
+ return false;
+ /* sct */
+ if(in.flags & soc_desc_v1::REG_HAS_SCT)
+ {
+ out.node.resize(1);
+ out.node[0].name = "SCT";
+ out.node[0].instance.resize(3);
+ const char *names[3] = {"SET", "CLR", "TOG"};
+ for(size_t i = 0; i < 3; i++)
+ {
+ out.node[0].instance[i].name = names[i];
+ out.node[0].instance[i].type = instance_t::SINGLE;
+ out.node[0].instance[i].addr = 4 + i *4;
+ }
+ }
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_dev_addr_t& in, instance_t& out, error_context_t& ctx)
+{
+ out.name = in.name;
+ out.type = instance_t::SINGLE;
+ out.addr = in.addr;
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_dev_t& in, node_t& out, error_context_t& ctx,
+ std::string _loc)
+{
+ std::string loc = _loc + "." + in.name;
+ if(!in.version.empty())
+ ctx.add(error_t(error_t::INFO, loc, "dropped version"));
+ out.name = in.name;
+ out.title = in.long_name;
+ out.desc = in.desc;
+ out.instance.resize(in.addr.size());
+ for(size_t i = 0; i < in.addr.size(); i++)
+ if(!convert_v1_to_v2(in.addr[i], out.instance[i], ctx))
+ return false;
+ out.node.resize(in.reg.size());
+ for(size_t i = 0; i < in.reg.size(); i++)
+ if(!convert_v1_to_v2(in.reg[i], out.node[i], ctx, loc))
+ return false;
+ return true;
+}
+
+bool convert_v1_to_v2(const soc_desc_v1::soc_t& in, soc_t& out, error_context_t& ctx)
+{
+ out.name = in.name;
+ out.title = in.desc;
+ out.node.resize(in.dev.size());
+ for(size_t i = 0; i < in.dev.size(); i++)
+ if(!convert_v1_to_v2(in.dev[i], out.node[i], ctx, in.name))
+ return false;
+ return true;
+}
+
+int do_convert(int argc, char **argv)
+{
+ if(argc != 2)
+ return printf("convert mode expects two arguments\n");
+ soc_desc_v1::soc_t soc;
+ if(!soc_desc_v1::parse_xml(argv[0], soc))
+ return printf("cannot read file '%s'\n", argv[0]);
+ error_context_t ctx;
+ soc_t new_soc;
+ if(!convert_v1_to_v2(soc, new_soc, ctx))
+ {
+ print_context(ctx);
+ return printf("cannot convert from v1 to v2\n");
+ }
+ if(!produce_xml(argv[1], new_soc, ctx))
+ {
+ print_context(ctx);
+ return printf("cannot write file '%s'\n", argv[1]);
+ }
+ print_context(ctx);
+ return 0;
+}
+
+int do_read(int argc, char **argv)
+{
+ for(int i = 0; i < argc; i++)
+ {
+ error_context_t ctx;
+ soc_t soc;
+ bool ret = parse_xml(argv[i], soc, ctx);
+ if(ctx.count() != 0)
+ printf("In file %s:\n", argv[i]);
+ print_context(ctx);
+ if(!ret)
+ {
+ printf("cannot parse file '%s'\n", argv[i]);
+ continue;
+ }
+ }
+ return 0;
+}
+
+int do_eval(int argc, char **argv)
+{
+ std::map< std::string, soc_word_t > map;
+ for(int i = 0; i < argc; i++)
+ {
+ std::string formula(argv[i]);
+ soc_word_t result;
+ if(strcmp(argv[i], "--var") == 0)
+ {
+ if(i + 1 >= argc)
+ break;
+ i++;
+ std::string str(argv[i]);
+ size_t pos = str.find('=');
+ if(pos == std::string::npos)
+ {
+ printf("invalid variable string '%s'\n", str.c_str());
+ continue;
+ }
+ std::string name = str.substr(0, pos);
+ std::string val = str.substr(pos + 1);
+ char *end;
+ soc_word_t v = strtoul(val.c_str(), &end, 0);
+ if(*end)
+ {
+ printf("invalid variable string '%s'\n", str.c_str());
+ continue;
+ }
+ printf("%s = %#lx\n", name.c_str(), (unsigned long)v);
+ map[name] = v;
+ continue;
+ }
+ error_context_t ctx;
+ if(!evaluate_formula(formula, map, result, "", ctx))
+ {
+ print_context(ctx);
+ printf("cannot parse '%s'\n", formula.c_str());
+ }
+ else
+ printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result);
+ }
+ return 0;
+}
+
+int do_write(int argc, char **argv)
+{
+ if(argc != 2)
+ return printf("write mode expects two arguments\n");
+ soc_t soc;
+ error_context_t ctx;
+ if(!parse_xml(argv[0], soc, ctx))
+ {
+ print_context(ctx);
+ return printf("cannot read file '%s'\n", argv[0]);
+ }
+ if(!produce_xml(argv[1], soc, ctx))
+ {
+ print_context(ctx);
+ return printf("cannot write file '%s'\n", argv[1]);
+ }
+ print_context(ctx);
+ return 0;
+}
+
+void check_name(const std::string& path, const std::string& name, error_context_t& ctx)
+{
+ if(name.empty())
+ ctx.add(error_t(error_t::FATAL, path, "name is empty"));
+ for(size_t i = 0; i < name.size(); i++)
+ if(!isalnum(name[i]) && name[i] != '_')
+ ctx.add(error_t(error_t::FATAL, path, "name '" + name +
+ "' must only contain alphanumeric characters or '_'"));
+}
+
+void check_instance(const std::string& _path, const instance_t& inst, error_context_t& ctx)
+{
+ std::string path = _path + "." + inst.name;
+ check_name(path, inst.name, ctx);
+ if(inst.type == instance_t::RANGE)
+ {
+ if(inst.range.type == range_t::FORMULA)
+ {
+ check_name(path + ".<formula variable>", inst.range.variable, ctx);
+ /* try to parse formula */
+ std::map< std::string, soc_word_t> var;
+ var[inst.range.variable] = inst.range.first;
+ soc_word_t res;
+ if(!evaluate_formula(inst.range.formula, var, res, path + ".<formula>", ctx))
+ ctx.add(error_t(error_t::FATAL, path + ".<formula>",
+ "cannot evaluate formula"));
+ }
+ }
+}
+
+void check_field(const std::string& _path, const field_t& field, error_context_t& ctx)
+{
+ std::string path = _path + "." + field.name;
+ check_name(path, field.name, ctx);
+ if(field.width == 0)
+ ctx.add(error_t(error_t::WARNING, path, "field has width 0"));
+ soc_word_t max = field.bitmask() >> field.pos;
+ std::set< std::string > names;
+ std::map< soc_word_t, std::string > map;
+ for(size_t i = 0; i < field.enum_.size(); i++)
+ {
+ soc_word_t v = field.enum_[i].value;
+ std::string n = field.enum_[i].name;
+ std::string path_ = path + "." + n;
+ check_name(path_, n, ctx);
+ if(v > max)
+ ctx.add(error_t(error_t::FATAL, path_, "value does not fit into the field"));
+ if(names.find(n) != names.end())
+ ctx.add(error_t(error_t::FATAL, path, "duplicate name '" + n + "' in enums"));
+ names.insert(n);
+ if(map.find(v) != map.end())
+ ctx.add(error_t(error_t::WARNING, path, "'" + n + "' and '" + map[v] + "' have the same value"));
+ map[v] = n;
+ }
+}
+
+void check_register(const std::string& _path, const soc_desc::register_t& reg, error_context_t& ctx)
+{
+ std::string path = _path + ".<register>";
+ if(reg.width != 8 && reg.width != 16 && reg.width != 32)
+ ctx.add(error_t(error_t::WARNING, path, "width is not 8, 16 or 32"));
+ for(size_t i = 0; i < reg.field.size(); i++)
+ check_field(path, reg.field[i], ctx);
+ std::set< std::string > names;
+ soc_word_t bitmap = 0;
+ for(size_t i = 0; i < reg.field.size(); i++)
+ {
+ std::string n = reg.field[i].name;
+ if(names.find(n) != names.end())
+ ctx.add(error_t(error_t::FATAL, path, "duplicate name '" + n + "' in fields"));
+ if(reg.field[i].pos + reg.field[i].width > reg.width)
+ ctx.add(error_t(error_t::FATAL, path, "field '" + n + "' does not fit into the register"));
+ names.insert(n);
+ if(bitmap & reg.field[i].bitmask())
+ {
+ /* find the duplicate to ease debugging */
+ for(size_t j = 0; j < i; j++)
+ if(reg.field[j].bitmask() & reg.field[i].bitmask())
+ ctx.add(error_t(error_t::FATAL, path, "overlap between fields '" +
+ reg.field[j].name + "' and '" + n + "'"));
+ }
+ bitmap |= reg.field[i].bitmask();
+ }
+}
+
+void check_nodes(const std::string& path, const std::vector< node_t >& nodes,
+ error_context_t& ctx);
+
+void check_node(const std::string& _path, const node_t& node, error_context_t& ctx)
+{
+ std::string path = _path + "." + node.name;
+ check_name(_path, node.name, ctx);
+ if(node.instance.empty())
+ ctx.add(error_t(error_t::FATAL, path, "subnode with no instances"));
+ for(size_t j = 0; j < node.instance.size(); j++)
+ check_instance(path, node.instance[j], ctx);
+ for(size_t i = 0; i < node.register_.size(); i++)
+ check_register(path, node.register_[i], ctx);
+ check_nodes(path, node.node, ctx);
+}
+
+void check_nodes(const std::string& path, const std::vector< node_t >& nodes,
+ error_context_t& ctx)
+{
+ for(size_t i = 0; i < nodes.size(); i++)
+ check_node(path, nodes[i], ctx);
+ /* gather all instance names */
+ std::set< std::string > names;
+ for(size_t i = 0; i < nodes.size(); i++)
+ for(size_t j = 0; j < nodes[i].instance.size(); j++)
+ {
+ std::string n = nodes[i].instance[j].name;
+ if(names.find(n) != names.end())
+ ctx.add(error_t(error_t::FATAL, path, "duplicate instance name '" +
+ n + "' in subnodes"));
+ names.insert(n);
+ }
+ /* gather all node names */
+ names.clear();
+ for(size_t i = 0; i < nodes.size(); i++)
+ {
+ std::string n = nodes[i].name;
+ if(names.find(n) != names.end())
+ ctx.add(error_t(error_t::FATAL, path, "duplicate node name '" + n +
+ "' in subnodes"));
+ names.insert(n);
+ }
+}
+
+void do_check(soc_t& soc, error_context_t& ctx)
+{
+ check_name(soc.name, soc.name, ctx);
+ check_nodes(soc.name, soc.node, ctx);
+}
+
+int do_check(int argc, char **argv)
+{
+ for(int i = 0; i < argc; i++)
+ {
+ error_context_t ctx;
+ soc_t soc;
+ bool ret = parse_xml(argv[i], soc, ctx);
+ if(ret)
+ do_check(soc, ctx);
+ if(ctx.count() != 0)
+ printf("In file %s:\n", argv[i]);
+ print_context(ctx);
+ if(!ret)
+ {
+ printf("cannot parse file '%s'\n", argv[i]);
+ continue;
+ }
+ }
+ return 0;
+}
+
+const unsigned DUMP_NODES = 1 << 0;
+const unsigned DUMP_INSTANCES = 1 << 1;
+const unsigned DUMP_VERBOSE = 1 << 2;
+const unsigned DUMP_REGISTERS = 1 << 3;
+
+void print_path(node_ref_t node, bool nl = true)
+{
+ printf("%s", node.soc().get()->name.c_str());
+ std::vector< std::string > path = node.path();
+ for(size_t i = 0; i < path.size(); i++)
+ printf(".%s", path[i].c_str());
+ if(nl)
+ printf("\n");
+}
+
+void print_inst(node_inst_t inst, bool end = true)
+{
+ if(!inst.is_root())
+ {
+ print_inst(inst.parent(), false);
+ printf(".%s", inst.name().c_str());
+ if(inst.is_indexed())
+ printf("[%u]", (unsigned)inst.index());
+ }
+ else
+ {
+ printf("%s", inst.soc().get()->name.c_str());
+ }
+ if(end)
+ printf(" @ %#x\n", inst.addr());
+}
+
+void print_reg(register_ref_t reg, unsigned flags)
+{
+ if(!(flags & DUMP_REGISTERS))
+ return;
+ node_ref_t node = reg.node();
+ soc_desc::register_t *r = reg.get();
+ print_path(node, false);
+ printf(":width=%u\n", (unsigned)r->width);
+ std::vector< field_ref_t > fields = reg.fields();
+ for(size_t i = 0; i < fields.size(); i++)
+ {
+ field_t *f = fields[i].get();
+ print_path(node, false);
+ if(f->width == 1)
+ printf(":[%u]=", (unsigned)f->pos);
+ else
+ printf(":[%u-%u]=", (unsigned)(f->pos + f->width - 1), (unsigned)f->pos);
+ printf("%s\n", f->name.c_str());
+ }
+}
+
+void do_dump(node_ref_t node, unsigned flags)
+{
+ print_path(node);
+ if(node.reg().node() == node)
+ print_reg(node.reg(), flags);
+ std::vector< node_ref_t > children = node.children();
+ for(size_t i = 0; i < children.size(); i++)
+ do_dump(children[i], flags);
+}
+
+void do_dump(node_inst_t inst, unsigned flags)
+{
+ print_inst(inst);
+ std::vector< node_inst_t > children = inst.children();
+ for(size_t i = 0; i < children.size(); i++)
+ do_dump(children[i], flags);
+}
+
+void do_dump(soc_t& soc, unsigned flags)
+{
+ soc_ref_t ref(&soc);
+ if(flags & DUMP_NODES)
+ do_dump(ref.root(), flags);
+ if(flags & DUMP_INSTANCES)
+ do_dump(ref.root_inst(), flags);
+}
+
+int do_dump(int argc, char **argv)
+{
+ unsigned flags = 0;
+ int i = 0;
+ for(; i < argc; i++)
+ {
+ if(strcmp(argv[i], "--nodes") == 0)
+ flags |= DUMP_NODES;
+ else if(strcmp(argv[i], "--instances") == 0)
+ flags |= DUMP_INSTANCES;
+ else if(strcmp(argv[i], "--verbose") == 0)
+ flags |= DUMP_VERBOSE;
+ else if(strcmp(argv[i], "--registers") == 0)
+ flags |= DUMP_REGISTERS;
+ else
+ break;
+ }
+ if(i == argc)
+ {
+ printf("you must specify at least one file\n");
+ return 1;
+ }
+ for(; i < argc; i++)
+ {
+ error_context_t ctx;
+ soc_t soc;
+ bool ret = parse_xml(argv[i], soc, ctx);
+ if(ret)
+ do_dump(soc, flags);
+ if(ctx.count() != 0)
+ printf("In file %s:\n", argv[i]);
+ print_context(ctx);
+ if(!ret)
+ {
+ printf("cannot parse file '%s'\n", argv[i]);
+ continue;
+ }
+ }
+ return 0;
+}
+
+void usage()
+{
+ printf("usage: swiss_knife <mode> [options]\n");
+ printf("modes:\n");
+ printf(" read <files...>\n");
+ printf(" write <read file> <write file>\n");
+ printf(" eval [<formula>|--var <name>=<val>]...\n");
+ printf(" convert <input file> <output file>\n");
+ printf(" check <files...>\n");
+ printf(" dump [--nodes] [--instances] [--registers] [--verbose] <files...>\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ if(argc < 2)
+ usage();
+ std::string mode = argv[1];
+ if(mode == "read")
+ return do_read(argc - 2, argv + 2);
+ else if(mode == "write")
+ return do_write(argc - 2, argv + 2);
+ else if(mode == "eval")
+ return do_eval(argc - 2, argv + 2);
+ else if(mode == "convert")
+ return do_convert(argc - 2, argv + 2);
+ else if(mode == "check")
+ return do_check(argc - 2, argv + 2);
+ else if(mode == "dump")
+ return do_dump(argc - 2, argv + 2);
+ else
+ usage();
+ return 0;
+} \ No newline at end of file
diff --git a/utils/regtools/tester.cpp b/utils/regtools/tester_v1.cpp
index 1beba5fe9b..3ece431531 100644
--- a/utils/regtools/tester.cpp
+++ b/utils/regtools/tester_v1.cpp
@@ -18,12 +18,14 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "soc_desc.hpp"
+#include "soc_desc_v1.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <cstring>
+using namespace soc_desc_v1;
+
template< typename T >
bool build_map(const char *type, const std::vector< T >& vec,
std::map< std::string, size_t >& map)
@@ -244,9 +246,9 @@ int do_compare(int argc, char **argv)
if(argc != 2)
return printf("compare mode expects two arguments\n");
soc_t soc[2];
- if(!soc_desc_parse_xml(argv[0], soc[0]))
+ if(!parse_xml(argv[0], soc[0]))
return printf("cannot read file '%s'\n", argv[0]);
- if(!soc_desc_parse_xml(argv[1], soc[1]))
+ if(!parse_xml(argv[1], soc[1]))
return printf("cannot read file '%s'\n", argv[1]);
if(compare_soc(soc[0], soc[1]))
printf("Files are identical.\n");
@@ -258,9 +260,9 @@ int do_write(int argc, char **argv)
if(argc != 2)
return printf("write mode expects two arguments\n");
soc_t soc;
- if(!soc_desc_parse_xml(argv[0], soc))
+ if(!parse_xml(argv[0], soc))
return printf("cannot read file '%s'\n", argv[0]);
- if(!soc_desc_produce_xml(argv[1], soc))
+ if(!produce_xml(argv[1], soc))
return printf("cannot write file '%s'\n", argv[1]);
return 0;
}
@@ -270,7 +272,7 @@ int do_check(int argc, char **argv)
for(int i = 0; i < argc; i++)
{
soc_t soc;
- if(!soc_desc_parse_xml(argv[i], soc))
+ if(!parse_xml(argv[i], soc))
{
printf("cannot read file '%s'\n", argv[i]);
continue;
@@ -325,7 +327,7 @@ int do_eval(int argc, char **argv)
map[name] = v;
continue;
}
- if(!soc_desc_evaluate_formula(formula, map, result, error))
+ if(!evaluate_formula(formula, map, result, error))
printf("error: %s\n", error.c_str());
else
printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result);