From 0f701a64bee43e79f95970ae9c0ec43ea7fcdf17 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 6 Feb 2016 15:01:24 +0000 Subject: regtools: update v2 specification, library and tools A v2 register description file can now include register variants and instances addresses can now be a list (previously it could only be a stride or a formula). Update the library to deal with that. The convert option of swiss_knife was updated and one incompatible change was introduce: if a v1 device has several addresses, those are converted to a single v2 instance with list (instead of several single instances). This should have been the behaviour from the start. Swiss_knife can now also convert regdumps, in which case it needs to be given both the dump and register description file. Also introduce two register descriptions files (vsoc1000 and vsoc2000) which give more complicated examples of v2 register description files. Change-Id: Id9415b8363269ffaf9216abfc6dd1bd1adbfcf8d --- utils/regtools/desc/regs-example-v1.xml | 2 +- utils/regtools/desc/regs-example.xml | 153 ----------- utils/regtools/desc/regs-vsoc1000.xml | 248 +++++++++++++++++ utils/regtools/desc/regs-vsoc2000.xml | 396 ++++++++++++++++++++++++++ utils/regtools/desc/spec-2.0.txt | 30 ++ utils/regtools/include/soc_desc.hpp | 139 +++++++++- utils/regtools/include/soc_desc_v1.hpp | 2 +- utils/regtools/lib/soc_desc.cpp | 473 +++++++++++++++++++++++++++++--- utils/regtools/swiss_knife.cpp | 259 +++++++++++++++-- 9 files changed, 1491 insertions(+), 211 deletions(-) delete mode 100644 utils/regtools/desc/regs-example.xml create mode 100644 utils/regtools/desc/regs-vsoc1000.xml create mode 100644 utils/regtools/desc/regs-vsoc2000.xml diff --git a/utils/regtools/desc/regs-example-v1.xml b/utils/regtools/desc/regs-example-v1.xml index 4f3cf81ff2..fcb9f7f55e 100644 --- a/utils/regtools/desc/regs-example-v1.xml +++ b/utils/regtools/desc/regs-example-v1.xml @@ -1,5 +1,5 @@ - + diff --git a/utils/regtools/desc/regs-example.xml b/utils/regtools/desc/regs-example.xml deleted file mode 100644 index 6fb8f759fe..0000000000 --- a/utils/regtools/desc/regs-example.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - vsoc - Virtual SOC - Virtual SoC is a nice and powerful chip. - Amaury Pouly - ARM - 0.5 - - int - Interrupt Collector - The interrupt collector controls the routing of interrupt to the processor - - ICOLL - Interrupt collector -
0x80000000
-
- - status - read-only - Interrupt status register - - STATUS -
0x4
-
- - 8 - - VDDIO_BO - VDDIO brownout interrupt status - 0 - - -
- - enable - Interrupt enable register - - ENABLE -
0x8
-
- - 16 - - VDDIO_BO - VDDIO brownout interrupt enable - 0 - 2 - - DISABLED - Interrupt is disabled - 0 - - - ENABLED - Interrupt is enabled - 1 - - - NMI - Interrupt is non-maskable - 2 - - - - - set - 4 - - - clr - 8 - -
-
- - gpio - GPIO controller - A GPIO controller manages several ports - - CPU_GPIO - CPU GPIO controller 1 through 3 - - 1 - 3 - 0x80001000+(n-1)*0x1000 - - - - COP_GPIO - Companion processor GPIO controller - Although the companion processor GPIO controller is accessible from the CPU, it incurs an extra penalty on the bus -
0x90000000
-
- - port - GPIO port - - PORT - - 0 - 4 - 0 - 0x100 - - - - input - Input register - - IN -
0
-
- - 8 - - VALUE - 0 - 8 - - -
- - output_enable - Output enable register - - OE -
0x10
-
- - 8 - - ENABLE - 0 - 8 - - - - set -
4
-
- - clr -
8
-
- - mask -
12
-
-
-
-
-
\ No newline at end of file diff --git a/utils/regtools/desc/regs-vsoc1000.xml b/utils/regtools/desc/regs-vsoc1000.xml new file mode 100644 index 0000000000..d909d85b53 --- /dev/null +++ b/utils/regtools/desc/regs-vsoc1000.xml @@ -0,0 +1,248 @@ + + + vsoc1000 + Virtual SOC 1000 + Virtual SoC 1000 is a nice chip. Its dual-core architecture makes it super powerful. + Amaury Pouly + ARM + 0.5 + + int + Interrupt Collector + The interrupt collector controls the routing of the interrupts to the processors. It has 32 interrupts sources, which can be routed as FIQ or IRQ to the main processor or the coprocessor. + + ICOLL + Interrupt collector +
0x80000000
+
+ + ctrl + Control register + + CTRL +
0x0
+
+ + 8 + + CLKGATE + Clock gating control + 7 + + + SFTRST + Soft reset, the bit will automatically reset to 0 when reset is completed + 6 + + + set + 4 + + + clr + 8 + + +
+ + status + Interrupt status register + + STATUS +
0x10
+
+ + + STATUS + Bit is set to 1 is the interrupt is pending, write a 1 to the clear variant to clear it + 0 + 32 + + + clr + 8 + + +
+ + enable + Interrupt enable register + + ENABLE + + 0 + 32 + 0x20 + 0x10 + + + + 16 + This register controls the routing of the interrupt + + COP_PRIO + Coprocessor priority + 5 + 2 + + MASKED + Interrupt is masked + 0x0 + + + LOW + 0x1 + + + HIGH + 0x2 + + + NMI + Interrupt is non maskable + 0x3 + + + + COP_TYPE + Interrupt type + 4 + + IRQ + 0x0 + + + FIQ + 0x1 + + + + CPU_PRIO + CPU priority + 2 + 2 + + MASKED + Interrupt will never be sent to the CPU + 0x0 + + + LOW + 0x1 + + + HIGH + 0x2 + + + NMI + Interrupt is non maskable + 0x3 + + + + CPU_TYPE + Interrupt type + 1 + + IRQ + 0x0 + + + FIQ + 0x1 + + + + ENABLE + 0 + + + set + 4 + + + clr + 8 + + + +
+ + gpio + GPIO controller + A GPIO controller manages several ports + + CPU_GPIO + CPU GPIO controller 1 through 3 + + 1 + 3 + 0x80001000+(n-1)*0x1000 + + + + COP_GPIO + Companion processor GPIO controller + Although the companion processor GPIO controller is accessible from the CPU, it incurs an extra penalty on the bus +
0x90000000
+
+ + port + GPIO port + + PORT + + 0 + 2 + 0x0 + 0x100 + + + + input + Input register + + IN +
0x0
+
+ + 8 + + VALUE + 0 + 8 + + +
+ + output_enable + Output enable register + + OE +
0x10
+
+ + 8 + + ENABLE + 0 + 8 + + + set + 4 + + + clr + 8 + + + mask + 12 + + +
+
+
+
diff --git a/utils/regtools/desc/regs-vsoc2000.xml b/utils/regtools/desc/regs-vsoc2000.xml new file mode 100644 index 0000000000..bcd6d08d38 --- /dev/null +++ b/utils/regtools/desc/regs-vsoc2000.xml @@ -0,0 +1,396 @@ + + + vsoc2000 + Virtual SOC 2000 + Virtual SoC 2000 is a nice chip. Its quad-core architecture with trustzone makes it super powerful. + Amaury Pouly + ARM + 0.5 + + int + Interrupt Collector + The interrupt collector controls the routing of the interrupts to the processors. It has 32 interrupts sources, which can be routed as FIQ or IRQ to the either processor. + + ICOLL + Interrupt collector +
0x80000000
+
+ + ctrl + Control register + + CTRL +
0x0
+
+ + 8 + + CLKGATE + Clock gating control. This bit can be protected by TZ lock. + 7 + + + SFTRST + Soft reset, the bit will automatically reset to 0 when reset is completed. This bit can be protected by TZ lock. + 6 + + + TZ_LOCK + Trust Zone lock + 5 + + UNLOCKED + 0x0 + + + LOCKED + When the interrupt collector is locked, only a secured processor can modify protected fields. + 0x1 + + + + set + 4 + + + clr + 8 + + +
+ + status + Interrupt status register + + STATUS +
0x10
+
+ + + STATUS + Bit is set to 1 is the interrupt is pending, write a 1 to the clear variant to clear it. Secured interrupts can only be cleared or polled by secured processors (non-secure will always read 0 for those). + 0 + 32 + + + clr + 8 + + +
+ + enable + Interrupt enable register + + ENABLE + + 0 + 32 + 0x20 + 0x10 + + + + 16 + This register controls the routing of the interrupt + + CPU3_PRIO + Interrupt priority + 14 + 2 + + MASKED + Interrupt is masked + 0x0 + + + LOW + 0x1 + + + HIGH + 0x2 + + + NMI + Interrupt is non maskable + 0x3 + + + + CPU3_TYPE + Interrupt type + 13 + + IRQ + 0x0 + + + FIQ + 0x1 + + + + CPU3_TZ + Trust Zone interrupt: when set, only a secured processor can modify the parameters for secured interrupts. + 12 + + + CPU2_PRIO + 10 + 2 + + MASKED + Interrupt is masked + 0x0 + + + LOW + 0x1 + + + HIGH + 0x2 + + + NMI + Interrupt is non maskable + 0x3 + + + + CPU2_TYPE + Interrupt type + 9 + + IRQ + 0x0 + + + FIQ + 0x1 + + + + CPU2_TZ + Trust Zone interrupt: when set, only a secured processor can modify the parameters for secured interrupts. + 8 + + + CPU1_PRIO + Interrupt priority + 6 + 2 + + MASKED + Interrupt is masked + 0x0 + + + LOW + 0x1 + + + HIGH + 0x2 + + + NMI + Interrupt is non maskable + 0x3 + + + + CPU1_TYPE + Interrupt type + 5 + + IRQ + 0x0 + + + FIQ + 0x1 + + + + CPU1_TZ + Trust Zone interrupt: when set, only a secured processor can modify the parameters for secured interrupts. + 4 + + + CPU0_PRIO + Interrupt priority + 2 + 2 + + MASKED + Interrupt will never be sent to the CPU + 0x0 + + + LOW + 0x1 + + + HIGH + 0x2 + + + NMI + Interrupt is non maskable + 0x3 + + + + CPU0_TYPE + Interrupt type + 1 + + IRQ + 0x0 + + + FIQ + 0x1 + + + + CPU0_TZ + Trust Zone interrupt: when set, only a secured processor can modify the parameters for secured interrupts. + 0 + + + set + 4 + + + clr + 8 + + + +
+ + gpio + GPIO controller + A GPIO controller manages several ports. + + CPU_GPIO + CPU GPIO controllers 1 through 7 + + 1 + 8 + 0x80001000+(n-1)*0x1000 + + + + port + GPIO port + + PORT + + 0 + 2 + 0x0 + 0x100 + + + + input + Input register + + IN +
0x0
+
+ + 8 + + VALUE + 0 + 8 + + +
+ + output_enable + Output enable register + + OE +
0x10
+
+ + 8 + + ENABLE + 0 + 8 + + + set + 4 + + + clr + 8 + + + mask + 12 + + +
+
+
+ + tz + Trust Zone + + TZ +
0xa0000000
+
+ + ctrl + Control Register + + CTRL +
0x0
+
+ + 8 + + SCRATCH + TZ protected scratch value + 4 + 4 + + + DISABLE + One bit per CPU: set to 1 to prevent the processor from being able to enter TZ mode. Can only be set by a secured processor. By default all processors can enter TZ mode. + 0 + 4 + + +
+ + debug + Debug register + + DEBUG + Debug register + Don't touch it! + + 42 +
0x50
+
0x60
+
0x90
+
0x110
+
0x130
+
+
+ + 8 + +
+
+
diff --git a/utils/regtools/desc/spec-2.0.txt b/utils/regtools/desc/spec-2.0.txt index 79b9f6be44..be97fbc41a 100644 --- a/utils/regtools/desc/spec-2.0.txt +++ b/utils/regtools/desc/spec-2.0.txt @@ -120,6 +120,23 @@ usual arithmetic operators. The example below illustrate such a use: +In the case when the addresses do not follow a regular pattern or a formula would +be too complicated, it is always possible to specify the addresses as a list: + + + N + + F + + 0 +
0x50
+
0x60
+
0x90
+
0x110
+
+
+
+ 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 @@ -177,6 +194,7 @@ and an optional description. The example below illustrates all these concepts: 8 + This register controls the parameters of the interrupt: priority, IRQ/FIQ and enable MODE Interrupt mode @@ -218,6 +236,10 @@ and an optional description. The example below illustrates all these concepts: 1 + + set + 0x4 + In this example, the 8-bit registers has three fields: @@ -350,7 +372,15 @@ 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: +- desc: free form description of the register - field: field description +- variant: variant description + +Element: variant +-------------- +It must contain the following tags: +- type: name of type, only made of alphanumerical characters +- offset: offset with respect to register address Element: field -------------- diff --git a/utils/regtools/include/soc_desc.hpp b/utils/regtools/include/soc_desc.hpp index 66f2e3b6e1..f6c5cca28f 100644 --- a/utils/regtools/include/soc_desc.hpp +++ b/utils/regtools/include/soc_desc.hpp @@ -125,11 +125,30 @@ struct field_t } }; +/** Register variant information + * + * A register variant provides an alternative access to the register, potentially + * we special semantics. Although there are no constraints on the type string, + * the following types have well-defined semantics: + * - alias: the same register at another address + * - set: writing to this register will set the 1s bits and ignore the 0s + * - clr: writing to this register will clear the 1s bits and ignore the 0s + * - tog: writing to this register will toggle the 1s bits and ignore the 0s + */ +struct variant_t +{ + soc_id_t id; /** ID (must be unique among register variants) */ + std::string type; /** type of the variant */ + soc_addr_t offset; /** offset of the variant */ +}; + /** Register information */ struct register_t { size_t width; /** Size in bits */ + std::string desc; /** Optional description of the register */ std::vector< field_t > field; /** List of fields */ + std::vector< variant_t > variant; /** List of variants */ }; /** Node address range information */ @@ -138,16 +157,24 @@ struct range_t enum type_t { STRIDE, /** Addresses are given by a base address and a stride */ - FORMULA /** Addresses are given by a formula */ + FORMULA, /** Addresses are given by a formula */ + LIST, /** Addresses are given by a list */ }; type_t type; /** Range type */ size_t first; /** First index in the range */ - size_t count; /** Number of indexes in the range */ + size_t count; /** Number of indexes in the range (for STRIDE and 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) */ + std::vector< soc_word_t > list; /** Address list (for LIST) */ + + /** Return the number of indexes (based on count or list) */ + size_t size() + { + return type == LIST ? list.size() : count; + } }; /** Node instance information */ @@ -197,6 +224,12 @@ bool parse_xml(const std::string& filename, soc_t& soc, error_context_t& error_c /** 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); +/** Normalise a soc description by reordering elements so that: + * - nodes are sorted by lowest address of an instance + * - instances are sorted by lowest address + * - fields are sorted by last bit + * - enum are sorted by value */ +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, @@ -219,6 +252,8 @@ class soc_ref_t; class node_ref_t; class register_ref_t; class field_ref_t; +class enum_ref_t; +class variant_ref_t; class node_inst_t; /** SoC reference */ @@ -241,6 +276,9 @@ public: /** 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); } + bool operator<(const soc_ref_t& r) const { return m_soc < r.m_soc; } + /** Make this reference invalid */ + void reset(); }; /** SoC node reference @@ -265,8 +303,10 @@ public: 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 n-th parent node, 0-th is itself, 1-th is parent */ + node_ref_t parent(unsigned level = 1) const; + /** Returns reference depth, root is 0, below root is 1 and so on */ + unsigned depth() 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 */ @@ -280,6 +320,16 @@ public: /** 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); } + /** Delete the node (and children) pointed by the reference, invalidating it + * NOTE: if reference points to the root node, deletes all nodes + * NOTE: does nothing if the reference is not valid */ + void remove(); + /** Create a new child node and returns a reference to it */ + node_ref_t create() const; + /** Create a register and returns a reference to it */ + register_ref_t create_reg(size_t width = 32) const; + /** Make this reference invalid */ + void reset(); }; /** SoC register reference */ @@ -300,11 +350,24 @@ public: 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 list of references to the variants of the register */ + std::vector< variant_ref_t > variants() const; /** Returns a reference to a particular field */ field_ref_t field(const std::string& name) const; + /** Returns a reference to a particular variant */ + variant_ref_t variant(const std::string& type) 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); } + /** Delete the register pointed by the reference, invalidating it + * NOTE: does nothing if the reference is not valid */ + void remove(); + /** Create a new field and returns a reference to it */ + field_ref_t create_field() const; + /** Create a new variant and returns a reference to it */ + variant_ref_t create_variant() const; + /** Make this reference invalid */ + void reset(); }; /** SoC register field reference */ @@ -312,7 +375,7 @@ 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 */ + soc_id_t m_id; /* field id */ field_ref_t(register_ref_t reg, soc_id_t id); public: @@ -324,9 +387,67 @@ public: field_t *get() const; /** Returns a reference to the register containing the field */ register_ref_t reg() const; + /** Returns a list of references to the enums of the field */ + std::vector< enum_ref_t > enums() 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); } + /** Make this reference invalid */ + void reset(); + /** Create a new enum and returns a reference to it */ + enum_ref_t create_enum() const; +}; + +/** SoC register field enum reference */ +class enum_ref_t +{ + friend class field_ref_t; + field_ref_t m_field; /* reference to the field */ + soc_id_t m_id; /* enum id */ + + enum_ref_t(field_ref_t reg, soc_id_t id); +public: + /** Builds an invalid reference */ + enum_ref_t(); + /** Check whether this reference is valid/exists */ + bool valid() const; + /** Returns a pointer to the enum, or 0 */ + enum_t *get() const; + /** Returns a reference to the field containing the enum */ + field_ref_t field() 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); } + /** Make this reference invalid */ + void reset(); +}; + +/** SoC register variant reference */ +class variant_ref_t +{ + friend class register_ref_t; + register_ref_t m_reg; /* reference to the register */ + soc_id_t m_id; /* variant name */ + + variant_ref_t(register_ref_t reg, soc_id_t id); +public: + /** Builds an invalid reference */ + variant_ref_t(); + /** Check whether this reference is valid/exists */ + bool valid() const; + /** Returns a pointer to the variant, or 0 */ + variant_t *get() const; + /** Returns a reference to the register containing the field */ + register_ref_t reg() const; + /** Returns variant type */ + std::string type() const; + /** Returns variant offset */ + soc_word_t offset() const; + /** Compare this reference to another */ + bool operator==(const variant_ref_t& r) const; + inline bool operator!=(const variant_ref_t& r) const { return !operator==(r); } + /** Make this reference invalid */ + void reset(); }; /** SoC node instance @@ -353,8 +474,10 @@ public: 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 reference to the n-th parent instance, 0-th is itself, and so on */ + node_inst_t parent(unsigned level = 1) const; + /** Returns reference depth, 0 is root, and so on */ + unsigned depth() const; /** Returns a pointer to the instance of the node, or 0 */ instance_t *get() const; /** Returns the address of this instance */ @@ -377,6 +500,8 @@ public: /** 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); } + /** Make this reference invalid */ + void reset(); }; } // soc_desc diff --git a/utils/regtools/include/soc_desc_v1.hpp b/utils/regtools/include/soc_desc_v1.hpp index 33368e88d4..b9185e35dd 100644 --- a/utils/regtools/include/soc_desc_v1.hpp +++ b/utils/regtools/include/soc_desc_v1.hpp @@ -214,7 +214,7 @@ 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 produce_xml(const std::string& filename, const soc_t& soc); -/** Normalise a soc description by reordering elemnts so that: +/** Normalise a soc description by reordering elements so that: * - devices are sorted by first name * - registers are sorted by first address * - fields are sorted by last bit diff --git a/utils/regtools/lib/soc_desc.cpp b/utils/regtools/lib/soc_desc.cpp index 4e0e46f00e..e6c58d4814 100644 --- a/utils/regtools/lib/soc_desc.cpp +++ b/utils/regtools/lib/soc_desc.cpp @@ -308,13 +308,28 @@ bool parse_field_elem(xmlNode *node, field_t& field, error_context_t& ctx) return ret; } +bool parse_variant_elem(xmlNode *node, variant_t& variant, error_context_t& ctx) +{ + bool ret = true; + bool has_type = false, has_offset = false; + BEGIN_NODE_MATCH(node->children) + MATCH_UNIQUE_TEXT_NODE("type", variant.type, has_type, parse_name_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("offset", variant.offset, has_offset, parse_unsigned_elem, ctx) + END_NODE_MATCH() + CHECK_HAS(node, "type", has_type, ctx) + CHECK_HAS(node, "offset", has_offset, ctx) + return ret; +} + bool parse_register_elem(xmlNode *node, register_t& reg, error_context_t& ctx) { bool ret = true; - bool has_width = false; + bool has_width = false, has_desc = false; BEGIN_NODE_MATCH(node->children) + MATCH_UNIQUE_TEXT_NODE("desc", reg.desc, has_desc, parse_text_elem, ctx) MATCH_UNIQUE_TEXT_NODE("width", reg.width, has_width, parse_unsigned_elem, ctx) MATCH_ELEM_NODE("field", reg.field, parse_field_elem, ctx) + MATCH_ELEM_NODE("variant", reg.variant, parse_variant_elem, ctx) END_NODE_MATCH() if(!has_width) reg.width = 32; @@ -344,21 +359,37 @@ bool parse_range_elem(xmlNode *node, range_t& range, error_context_t& 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) + MATCH_TEXT_NODE("address", range.list, parse_unsigned_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 or vars; + vars[inst.range.variable] = inst.range.first; + error_context_t ctx; + if(!evaluate_formula(inst.range.formula, vars, res, "", ctx)) + return 0xffffffff; + return res; + } + + /* sort instances by first address */ + bool operator()(const instance_t& a, const instance_t& b) const + { + return first_addr(a) < first_addr(b); + } + + /* sort nodes by first address of first instance (which is the lowest of + * any instance if instances are sorted) */ + bool operator()(const node_t& a, const node_t& b) const + { + /* borderline cases: no instances is lower than with instances */ + if(a.instance.size() == 0) + return b.instance.size() > 0; + if(b.instance.size() == 0) + return false; + return first_addr(a.instance[0]) < first_addr(b.instance[0]); + } + + /* sort fields by decreasing position */ + bool operator()(const field_t& a, const field_t& b) const + { + return a.pos > b.pos; + } + + /* sort enum values by value */ + bool operator()(const enum_t& a, const enum_t& b) const + { + return a.value < b.value; + } +}; + +void normalize(field_t& field) +{ + std::sort(field.enum_.begin(), field.enum_.end(), soc_sorter()); +} + +void normalize(register_t& reg) +{ + for(size_t i = 0; i < reg.field.size(); i++) + normalize(reg.field[i]); + std::sort(reg.field.begin(), reg.field.end(), soc_sorter()); +} + +void normalize(node_t& node) +{ + for(size_t i = 0; i < node.register_.size(); i++) + normalize(node.register_[i]); + for(size_t i = 0; i < node.node.size(); i++) + normalize(node.node[i]); + std::sort(node.node.begin(), node.node.end(), soc_sorter()); + std::sort(node.instance.begin(), node.instance.end(), soc_sorter()); +} + +} + +void normalize(soc_t& soc) +{ + for(size_t i = 0; i < soc.node.size(); i++) + normalize(soc.node[i]); + std::sort(soc.node.begin(), soc.node.end(), soc_sorter()); +} + /** * Producer */ @@ -488,17 +605,20 @@ int produce_range(xmlTextWriterPtr writer, const range_t& range, error_context_t SAFE(xmlTextWriterStartElement(writer, BAD_CAST "range")); /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "first", "%lu", range.first)); - /* */ - SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); - /* */ if(range.type == range_t::STRIDE) { + /* */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); + /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base)); + /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride)); } /* */ else if(range.type == range_t::FORMULA) { + /* */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); /* */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula")); /* variable */ @@ -508,6 +628,11 @@ int produce_range(xmlTextWriterPtr writer, const range_t& range, error_context_t /* */ SAFE(xmlTextWriterEndElement(writer)); } + else if(range.type == range_t::LIST) + { + for(size_t i = 0; i < range.list.size(); i++) + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", range.list[i])); + } /* */ SAFE(xmlTextWriterEndElement(writer)); @@ -575,6 +700,19 @@ int produce_field(xmlTextWriterPtr writer, const field_t& field, error_context_t return 0; } +int produce_variant(xmlTextWriterPtr writer, const variant_t& variant, error_context_t& ctx) +{ + /* */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "variant")); + /* */ + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "type", BAD_CAST variant.type.c_str())); + /* */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "offset", "%lu", (unsigned long)variant.offset)); + /* */ + SAFE(xmlTextWriterEndElement(writer)); + return 0; +} + int produce_register(xmlTextWriterPtr writer, const register_t& reg, error_context_t& ctx) { /* */ @@ -582,9 +720,15 @@ int produce_register(xmlTextWriterPtr writer, const register_t& reg, error_conte /* */ if(reg.width != 32) SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", reg.width)); + /* */ + if(!reg.desc.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str())); /* fields */ for(size_t i = 0; i < reg.field.size(); i++) SAFE(produce_field(writer, reg.field[i], ctx)); + /* variants */ + for(size_t i = 0; i < reg.variant.size(); i++) + SAFE(produce_variant(writer, reg.variant[i], ctx)); /* */ SAFE(xmlTextWriterEndElement(writer)); return 0; @@ -670,6 +814,24 @@ Lerr: return false; } +/** + * utils + */ + +namespace +{ + +template< typename T > +soc_id_t gen_fresh_id(const std::vector< T >& list) +{ + soc_id_t id = 0; + for(size_t i = 0; i < list.size(); i++) + id = std::max(id, list[i].id); + return id + 1; +} + +} + /** * soc_ref_t */ @@ -707,6 +869,11 @@ node_inst_t soc_ref_t::root_inst() const return node_inst_t(*this); } +void soc_ref_t::reset() +{ + m_soc = 0; +} + /** * node_ref_t */ @@ -733,6 +900,11 @@ bool node_ref_t::is_root() const return m_path.empty(); } +void node_ref_t::reset() +{ + m_soc.reset(); +} + namespace { @@ -789,14 +961,20 @@ soc_ref_t node_ref_t::soc() const return m_soc; } -node_ref_t node_ref_t::parent() const +node_ref_t node_ref_t::parent(unsigned level) const { + if(level > depth()) + return node_ref_t(); std::vector< soc_id_t > path = m_path; - if(!path.empty()) - path.pop_back(); + path.resize(depth() - level); return node_ref_t(m_soc, path); } +unsigned node_ref_t::depth() const +{ + return m_path.size(); +} + register_ref_t node_ref_t::reg() const { node_t *n = get(); @@ -808,6 +986,18 @@ register_ref_t node_ref_t::reg() const return register_ref_t(*this); } +register_ref_t node_ref_t::create_reg(size_t width) const +{ + node_t *n = get(); + if(n == 0) + return register_ref_t(); + if(!n->register_.empty()) + return register_ref_t(); + n->register_.resize(1); + n->register_[0].width = width; + return register_ref_t(*this); +} + node_ref_t node_ref_t::child(const std::string& name) const { /* check the node exists */ @@ -867,6 +1057,41 @@ bool node_ref_t::operator==(const node_ref_t& ref) const return m_soc == ref.m_soc && m_path == ref.m_path; } +void node_ref_t::remove() +{ + if(is_root()) + { + soc_t *s = soc().get(); + if(s) + s->node.clear(); + } + else + { + std::vector< node_t > *list = get_children(parent()); + if(list == 0) + return; + for(size_t i = 0; i < list->size(); i++) + if((*list)[i].id == m_path.back()) + { + list->erase(list->begin() + i); + return; + } + } +} + +node_ref_t node_ref_t::create() const +{ + std::vector< node_t > *list = get_children(*this); + if(list == 0) + return node_ref_t(); + node_t n; + n.id = gen_fresh_id(*list); + list->push_back(n); + std::vector< soc_id_t > path = m_path; + path.push_back(n.id); + return node_ref_t(soc(), path); +} + /** * register_ref_t */ @@ -885,6 +1110,11 @@ bool register_ref_t::valid() const return get() != 0; } +void register_ref_t::reset() +{ + m_node.reset(); +} + register_t *register_ref_t::get() const { node_t *n = m_node.get(); @@ -909,6 +1139,17 @@ std::vector< field_ref_t > register_ref_t::fields() const return fields; } +std::vector< variant_ref_t > register_ref_t::variants() const +{ + std::vector< variant_ref_t > variants; + register_t *r = get(); + if(r == 0) + return variants; + for(size_t i = 0; i < r->variant.size(); i++) + variants.push_back(variant_ref_t(*this, r->variant[i].id)); + return variants; +} + field_ref_t register_ref_t::field(const std::string& name) const { register_t *r = get(); @@ -920,6 +1161,46 @@ field_ref_t register_ref_t::field(const std::string& name) const return field_ref_t(); } +variant_ref_t register_ref_t::variant(const std::string& type) const +{ + register_t *r = get(); + if(r == 0) + return variant_ref_t(); + for(size_t i = 0; i < r->variant.size(); i++) + if(r->variant[i].type == type) + return variant_ref_t(*this, r->variant[i].id); + return variant_ref_t(); +} + +void register_ref_t::remove() +{ + node_t *n = node().get(); + if(n) + n->register_.clear(); +} + +field_ref_t register_ref_t::create_field() const +{ + register_t *r = get(); + if(r == 0) + return field_ref_t(); + field_t f; + f.id = gen_fresh_id(r->field); + r->field.push_back(f); + return field_ref_t(*this, f.id); +} + +variant_ref_t register_ref_t::create_variant() const +{ + register_t *r = get(); + if(r == 0) + return variant_ref_t(); + variant_t v; + v.id = gen_fresh_id(r->variant); + r->variant.push_back(v); + return variant_ref_t(*this, v.id); +} + /** * field_ref_t */ @@ -938,6 +1219,11 @@ bool field_ref_t::valid() const return get() != 0; } +void field_ref_t::reset() +{ + m_reg.reset(); +} + field_t *field_ref_t::get() const { register_t *reg = m_reg.get(); @@ -949,11 +1235,123 @@ field_t *field_ref_t::get() const return 0; } +std::vector< enum_ref_t > field_ref_t::enums() const +{ + std::vector< enum_ref_t > enums; + field_t *f = get(); + if(f == 0) + return enums; + for(size_t i = 0; i < f->enum_.size(); i++) + enums.push_back(enum_ref_t(*this, f->enum_[i].id)); + return enums; +} + register_ref_t field_ref_t::reg() const { return m_reg; } +enum_ref_t field_ref_t::create_enum() const +{ + field_t *f = get(); + if(f == 0) + return enum_ref_t(); + enum_t e; + e.id = gen_fresh_id(f->enum_); + f->enum_.push_back(e); + return enum_ref_t(*this, e.id); +} + +/** + * enum_ref_t + */ + +enum_ref_t::enum_ref_t(field_ref_t field, soc_id_t id) + :m_field(field), m_id(id) +{ +} + +enum_ref_t::enum_ref_t() +{ +} + +bool enum_ref_t::valid() const +{ + return get() != 0; +} + +void enum_ref_t::reset() +{ + m_field.reset(); +} + +enum_t *enum_ref_t::get() const +{ + field_t *field = m_field.get(); + if(field == 0) + return 0; + for(size_t i = 0; i < field->enum_.size(); i++) + if(field->enum_[i].id == m_id) + return &field->enum_[i]; + return 0; +} + +field_ref_t enum_ref_t::field() const +{ + return m_field; +} + +/** + * variant_ref_t + */ + +variant_ref_t::variant_ref_t(register_ref_t reg, soc_id_t id) + :m_reg(reg), m_id(id) +{ +} + +variant_ref_t::variant_ref_t() +{ +} + +bool variant_ref_t::valid() const +{ + return get() != 0; +} + +void variant_ref_t::reset() +{ + m_reg.reset(); +} + +variant_t *variant_ref_t::get() const +{ + register_t *reg = m_reg.get(); + if(reg == 0) + return 0; + for(size_t i = 0; i < reg->variant.size(); i++) + if(reg->variant[i].id == m_id) + return ®->variant[i]; + return 0; +} + +register_ref_t variant_ref_t::reg() const +{ + return m_reg; +} + +std::string variant_ref_t::type() const +{ + variant_t *v = get(); + return v ? v->type : std::string(); +} + +soc_word_t variant_ref_t::offset() const +{ + variant_t *v = get(); + return v ? v->offset : 0; +} + /** * node_inst_t */ @@ -965,8 +1363,8 @@ const size_t INST_NO_INDEX = std::numeric_limits::max(); 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; + if(index < range.first || index >= range.first + range.size()) + return false; switch(range.type) { case range_t::STRIDE: @@ -983,6 +1381,9 @@ bool get_inst_addr(range_t& range, size_t index, soc_addr_t& addr) addr += res; return true; } + case range_t::LIST: + addr += range.list[index - range.first]; + return true; default: return false; } @@ -1030,6 +1431,11 @@ bool node_inst_t::valid() const return is_root() || get() != 0; } +void node_inst_t::reset() +{ + m_node.reset(); +} + node_ref_t node_inst_t::node() const { return m_node; @@ -1045,15 +1451,20 @@ bool node_inst_t::is_root() const return m_node.is_root(); } -node_inst_t node_inst_t::parent() const +node_inst_t node_inst_t::parent(unsigned level) const { + if(level > depth()) + return node_inst_t(); 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); + ids.resize(depth() - level); + indexes.resize(depth() - level); + return node_inst_t(m_node.parent(level), ids, indexes); +} + +unsigned node_inst_t::depth() const +{ + return m_id_path.size(); } instance_t *node_inst_t::get() const @@ -1069,7 +1480,7 @@ instance_t *node_inst_t::get() const soc_addr_t node_inst_t::addr() const { - if(is_root()) + if(!valid() || is_root()) return 0; soc_addr_t addr = parent().addr(); if(!get_inst_addr(get(), m_index_path.back(), addr)) @@ -1100,7 +1511,7 @@ node_inst_t node_inst_t::child(const std::string& name, size_t index) const 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); + indexes.push_back(index); return node_inst_t(child_node, ids, indexes); } child_node.m_path.pop_back(); @@ -1133,7 +1544,7 @@ std::vector< node_inst_t > node_inst_t::children() const i_path.pop_back(); break; case instance_t::RANGE: - for(size_t i = 0; i < inst.range.count; i++) + for(size_t i = 0; i < inst.range.size(); i++) { i_path.push_back(inst.range.first + i); list.push_back(node_inst_t(child_node, n_path, i_path)); diff --git a/utils/regtools/swiss_knife.cpp b/utils/regtools/swiss_knife.cpp index eaa2519a27..fb65b5ca24 100644 --- a/utils/regtools/swiss_knife.cpp +++ b/utils/regtools/swiss_knife.cpp @@ -20,11 +20,14 @@ ****************************************************************************/ #include "soc_desc.hpp" #include "soc_desc_v1.hpp" -#include -#include +#include +#include #include #include #include +#include +#include +#include using namespace soc_desc; @@ -88,7 +91,6 @@ bool convert_v1_to_v2(const soc_desc_v1::soc_reg_t& in, node_t& out, error_conte { 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()); @@ -133,6 +135,7 @@ bool convert_v1_to_v2(const soc_desc_v1::soc_reg_t& in, node_t& out, error_conte } out.register_.resize(1); out.register_[0].width = 32; + out.register_[0].desc = in.desc; 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)) @@ -140,15 +143,12 @@ bool convert_v1_to_v2(const soc_desc_v1::soc_reg_t& in, node_t& out, error_conte /* 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"}; + out.register_[0].variant.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; + out.register_[0].variant[i].type = names[i]; + out.register_[0].variant[i].offset = 4 + i *4; } } return true; @@ -171,10 +171,23 @@ bool convert_v1_to_v2(const soc_desc_v1::soc_dev_t& in, node_t& out, error_conte 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.instance.resize(1); + if(in.addr.size() == 1) + { + out.instance[0].type = instance_t::SINGLE; + out.instance[0].name = in.addr[0].name; + out.instance[0].addr = in.addr[0].addr; + } + else + { + out.instance[0].type = instance_t::RANGE; + out.instance[0].name = in.name; + out.instance[0].range.type = range_t::LIST; + out.instance[0].range.first = 1; + out.instance[0].range.list.resize(in.addr.size()); + for(size_t i = 0; i < in.addr.size(); i++) + out.instance[0].range.list[i] = in.addr[i].addr; + } 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)) @@ -195,8 +208,21 @@ bool convert_v1_to_v2(const soc_desc_v1::soc_t& in, soc_t& out, error_context_t& int do_convert(int argc, char **argv) { - if(argc != 2) - return printf("convert mode expects two arguments\n"); + std::vector< std::string > authors; + std::string version; + while(argc >= 2) + { + if(strcmp(argv[0], "--author") == 0) + authors.push_back(argv[1]); + else if(strcmp(argv[0], "--version") == 0) + version = argv[1]; + else + break; + argc -= 2; + argv += 2; + } + if(argc < 2) + return printf("convert mode expects at least one description file and an output file\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]); @@ -207,6 +233,8 @@ int do_convert(int argc, char **argv) print_context(ctx); return printf("cannot convert from v1 to v2\n"); } + new_soc.author = authors; + new_soc.version = version; if(!produce_xml(argv[1], new_soc, ctx)) { print_context(ctx); @@ -392,7 +420,7 @@ void check_node(const std::string& _path, const node_t& node, error_context_t& c 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")); + ctx.add(error_t(error_t::WARNING, 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++) @@ -506,6 +534,12 @@ void print_reg(register_ref_t reg, unsigned flags) printf(":[%u-%u]=", (unsigned)(f->pos + f->width - 1), (unsigned)f->pos); printf("%s\n", f->name.c_str()); } + std::vector< variant_ref_t > variants = reg.variants(); + for(size_t i = 0; i < variants.size(); i++) + { + print_path(node, false); + printf(":%s@+0x%x\n", variants[i].type().c_str(), variants[i].offset()); + } } void do_dump(node_ref_t node, unsigned flags) @@ -576,6 +610,178 @@ int do_dump(int argc, char **argv) return 0; } +std::string trim(const std::string& s) +{ + std::string ss = s.substr(s.find_first_not_of(" \t")); + return ss.substr(0, ss.find_last_not_of(" \t") + 1); +} + +bool parse_key(const std::string& key, std::string& dev, std::string& reg) +{ + if(key.substr(0, 3) != "HW.") + return false; + std::string s = key.substr(3); + size_t idx = s.find('.'); + if(idx == std::string::npos) + return false; + dev = s.substr(0, idx); + reg = s.substr(idx + 1); + return true; +} + +bool find_addr(const soc_desc_v1::soc_dev_t& dev, + const std::string& reg, soc_desc_v1::soc_addr_t& addr) +{ + for(size_t i = 0; i < dev.reg.size(); i++) + for(size_t j = 0; j < dev.reg[i].addr.size(); j++) + if(dev.reg[i].addr[j].name == reg) + { + addr += dev.reg[i].addr[j].addr; + return true; + } + return false; +} + +bool find_addr(const soc_desc_v1::soc_t& soc, const std::string& dev, + const std::string& reg, soc_desc_v1::soc_addr_t& addr) +{ + addr = 0; + for(size_t i = 0; i < soc.dev.size(); i++) + for(size_t j = 0; j < soc.dev[i].addr.size(); j++) + if(soc.dev[i].addr[j].name == dev) + { + addr += soc.dev[i].addr[j].addr; + return find_addr(soc.dev[i], reg, addr); + } + return false; +} + +int convert_dump(const std::map< std::string, std::string >& entries, + const soc_desc_v1::soc_t& soc, std::ofstream& fout) +{ + std::map< std::string, std::string >::const_iterator it = entries.begin(); + for(; it != entries.end(); ++it) + { + char *end; + soc_desc_v1::soc_word_t v = strtoul(it->second.c_str(), &end, 0); + if(*end != 0) + { + printf("because of invalid value '%s': ignore key '%s'\n", + it->second.c_str(), it->first.c_str()); + continue; + } + std::string dev, reg; + if(!parse_key(it->first, dev, reg)) + { + printf("invalid key format, ignore key '%s'\n", it->first.c_str()); + continue; + } + soc_desc_v1::soc_addr_t addr; + if(!find_addr(soc, dev, reg, addr)) + { + printf("cannot find register in description, ignore key '%s'\n", + it->first.c_str()); + continue; + } + fout << "0x" << std::hex << addr << " = 0x" << std::hex << v << "\n"; + } + return 0; +} + +int do_convertdump(int argc, char **argv) +{ + if(argc < 3) + { + printf("you must specify at least one description file, one input file and one output file\n"); + return 1; + } + std::vector< soc_desc_v1::soc_t > socs; + for(int i = 0; i < argc - 2; i++) + { + socs.resize(socs.size() + 1); + if(!parse_xml(argv[i], socs.back())) + { + socs.pop_back(); + printf("cannot parse description file '%s'\n", argv[i]); + } + } + std::ifstream fin(argv[argc - 2]); + if(!fin) + { + printf("cannot open input file\n"); + return 1; + } + std::map< std::string, std::string > entries; + std::string line; + while(std::getline(fin, line)) + { + size_t idx = line.find('='); + if(idx == std::string::npos) + { + printf("ignore invalid line '%s'\n", line.c_str()); + continue; + } + std::string key = trim(line.substr(0, idx)); + std::string value = trim(line.substr(idx + 1)); + entries[key] = value; + } + if(entries.find("HW") == entries.end()) + { + printf("invalid dump file: missing HW key\n"); + return 1; + } + std::string soc = entries["HW"]; + soc_desc_v1::soc_t *psoc = 0; + for(size_t i = 0; i < socs.size(); i++) + if(socs[i].name == soc) + psoc = &socs[i]; + if(psoc == 0) + { + printf("cannot convert dump: please provide the description file for the soc '%s'\n", soc.c_str()); + return 1; + } + entries.erase(entries.find("HW")); + std::ofstream fout(argv[argc - 1]); + if(!fout) + { + printf("cannot open output file\n"); + return 1; + } + fout << "soc = " << soc << "\n"; + return convert_dump(entries, *psoc, fout); +} + +int do_normalize(int argc, char **argv) +{ + if(argc != 2) + { + printf("normalize takes two arguments\n"); + return 1; + } + error_context_t ctx; + soc_t soc; + bool ret = parse_xml(argv[0], soc, ctx); + if(ctx.count() != 0) + printf("In file %s:\n", argv[0]); + print_context(ctx); + if(!ret) + { + printf("cannot parse file '%s'\n", argv[1]); + return 2; + } + normalize(soc); + ret = produce_xml(argv[1], soc, ctx); + if(ctx.count() != 0) + printf("In file %s:\n", argv[1]); + print_context(ctx); + if(!ret) + { + printf("cannot write file '%s'\n", argv[1]); + return 3; + } + return 0; +} + void usage() { printf("usage: swiss_knife [options]\n"); @@ -583,9 +789,22 @@ void usage() printf(" read \n"); printf(" write \n"); printf(" eval [|--var =]...\n"); - printf(" convert \n"); + printf(" convert [--author ] [--version ] \n"); printf(" check \n"); printf(" dump [--nodes] [--instances] [--registers] [--verbose] \n"); + printf(" convertdump ... \n"); + printf(" normalize \n"); + printf("\n"); + printf("The following operations are performed in each mode:\n"); + printf("* read: open and parse the files, reports any obvious errors\n"); + printf("* write: open, parse a file and write it back, checks the parser/generator match\n"); + printf("* eval: evaluate a formula with the formula parser\n"); + printf("* convert: convert a description file from version 1 to version 2\n"); + printf("* check: performs deep checks on description files\n"); + printf("* dump: debug tool to dump internal structures\n"); + printf("* convertdump: convert a register dump from version 1 to version 2\n"); + printf(" NOTE: description file must be a v1 file\n"); + printf("* normalize: normalise a description file\n"); exit(1); } @@ -606,6 +825,10 @@ int main(int argc, char **argv) return do_check(argc - 2, argv + 2); else if(mode == "dump") return do_dump(argc - 2, argv + 2); + else if(mode == "convertdump") + return do_convertdump(argc - 2, argv + 2); + else if(mode == "normalize") + return do_normalize(argc - 2, argv + 2); else usage(); return 0; -- cgit v1.2.3