big initial commit, thanks kprotty for helping with MmioInt()

wch-ch32v003
Matt Knight 3 years ago committed by Matt Knight
parent 39f633642d
commit fed1a8f3fd

@ -0,0 +1,2 @@
zig-cache
zig-out

@ -0,0 +1,6 @@
[submodule "libs/zig-clap"]
path = libs/zig-clap
url = https://github.com/Hejsil/zig-clap.git
[submodule "libs/zig-libxml2"]
path = libs/zig-libxml2
url = https://github.com/mitchellh/zig-libxml2.git

@ -0,0 +1,86 @@
# regz
regz is a Zig code generator for microcontrollers. Vendors often publish files
that have the details of special function registers, for ARM this is called a
"System View Description" (SVD), and this tool outputs a single file for you to
start interacting with the hardware:
```zig
const regs = @import("nrf52.zig").registers;
pub fn main() void {
regs.P0.PIN_CNF[17].modify(.{
.DIR = 1,
.INPUT = 1,
.PULL = 0,
.DRIVE = 0,
.SENSE = 0,
});
regs.P0.OUT.modify(.{ .PIN17 = 1 });
}
```
NOTE: just including that file is not enough to run code on a microcontroller,
this is a fairly low-level tool and it is intended that the generated code be
used with [microzig](https://github.com/ZigEmbeddedGroup/microzig)
One can get the required SVD file from your vendor, or another good place is
[posborne/cmsis-svd](https://github.com/posborne/cmsis-svd/tree/master/data),
it's a python based SVD parser and they have a large number of files available.
## Building
regz targets zig master.
```
git clone --recursive https://github.com/ZigEmbeddedGroup/regz.git
zig build
```
## Using regz to generate code
Provide path on command line:
```
regz <path-to-svd> > my-chip.zig
```
Provide schema via stdin, must specify the schema type:
```
cat my-file.svd | regz --schema svd > my-chip.zig
```
### Does this work for RISC-V?
It seems that manufacturers are using SVD to represent registers on their
RISC-V based products despite it being an ARM standard. At best regz will
generate the register definitions without an interrupt table (for now), if you
run into problems issues will be warmly welcomed!
### How about AVR?
Atmel/Microchip publishes their register definitions for AVRs in ATDF, it is
not implemented, but we do plan on supporting it. There are tools like
[Rahix/atdf2svd](https://github.com/Rahix/atdf2svd) if you really can't wait to
get your hands dirty.
### What about MSP430?
TI does have another type of XML-based register schema, it is also
unimplemented but planned for support.
### Okay but I want [some other architecture/format]
The main idea is to target what LLVM can target, however Zig's C backend in
underway so it's likely more exotic architectures could be reached in the
future. If you know of any others we should look into, please make an issue!
## Roadmap
- SVD: mostly implemented and usable for mosts MCUs, but a few finishing touches in order to suss out any bugs:
- [ ] nested clusters
- [ ] order generated exactly as defined in schema
- [ ] finalize derivation of different components
- [ ] comprehensive suite of tests
- [ ] RISC-V interrupt table generation
- [ ] ATDF: Atmel's register schema format
- [ ] insert name of Texus Insturment's register schema format for MSP430

@ -0,0 +1,37 @@
const std = @import("std");
const libxml2 = @import("libs/zig-libxml2/libxml2.zig");
pub fn build(b: *std.build.Builder) !void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
const xml = try libxml2.create(b, target, mode, .{
.iconv = false,
.lzma = false,
.zlib = false,
});
xml.step.install();
const exe = b.addExecutable("regz", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.addPackagePath("clap", "libs/zig-clap/clap.zig");
xml.link(exe);
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const exe_tests = b.addTest("src/main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
}

@ -0,0 +1 @@
Subproject commit cf8a34d11f0520bdf2afc08eda88862597a88b23

@ -0,0 +1 @@
Subproject commit c2cf5ec294d08adfa0fc7aea7245a83871ed19f2

File diff suppressed because it is too large Load Diff

@ -0,0 +1,660 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2013-2019 ARM Limited. All rights reserved.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the License); you may
not use this file except in compliance with the License.
You may obtain a copy of the License at
www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an AS IS BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
$Date: 12. June 2021
$Revision: 1.3.7
Version 1.3.7:
- add CM55 as enumerated value for cpuNameType.
Version 1.3.6:
- add ARMV81MML as enumeration value for cpuNameType.
Version 1.3.5:
- add CM35P as enumeration value for cpuNameType.
Version 1.3.4:
- add dspPresent element to cpuType as SIMD instructions became optional for new processors.
Version 1.3.3:
- update file header to Apache 2.0 License
- add dimableIdentifierType, as a copy of previous identifierType adding "%s",
- update identifierType to only allow names without %s included.
- remove enumerationNameType.
- add headerEnumName to enumerationType and to dimArrayIndexType for peripheral arrays
overwriting hierarchically generated names
- add dimName to dimElementGroup. Only valid in <cluster> context, ignored otherwise.
Version 1.3.2:
adding dimIndexArray to peripheral-, cluster- and register-array to describe
enumeration of array indices.
Version 1.3.1:
fixed peripheral name element type to identifierType to support %s for peripheral arrays
added optional protection element to addressBlockType and added p=privileged
Version 1.3:
added dim to peripherals to describe an array of peripherals.
added nesting of clusters to support hierarchical register structures.
added protection element as part of the registerPropertiesGroup indicating
special permissions are required for accessing a register.
CPU Section extended with description of the Secure Attribution Unit.
Version 1.2:
Cortex-M7 support items have been added as optional tags for the device header file generation:
fpuDP, icachePresent, dcachePresent, itcmPresent, dtcmPresent
Version 1.1:
For backward compatibility all additional tags have been made optional.
Extensions may be mandatory for successful device header file generation
Other changes are related to some restructuring of the schema.
Note that the memory section has been removed since this would limit the
reuse of descriptions for a series of devices.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="qualified" version="1.3">
<!-- stringType requires a none empty string of a least one character length -->
<xs:simpleType name="stringType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="descriptionStringType">
<xs:restriction base="xs:string">
<xs:pattern value="[\p{IsBasicLatin}\p{IsLatin-1Supplement}]*" />
</xs:restriction>
</xs:simpleType>
<!-- cpuType specifies a selection of Cortex-M and Secure-Cores. This list will get extended as new processors are released -->
<xs:simpleType name="cpuNameType">
<xs:restriction base="xs:token">
<xs:enumeration value="CM0"/>
<xs:enumeration value="CM0PLUS"/>
<xs:enumeration value="CM0+"/>
<xs:enumeration value="CM1"/>
<xs:enumeration value="SC000"/>
<xs:enumeration value="CM23"/>
<xs:enumeration value="CM3"/>
<xs:enumeration value="CM33"/>
<xs:enumeration value="CM35P"/>
<xs:enumeration value="CM55"/>
<xs:enumeration value="SC300"/>
<xs:enumeration value="CM4"/>
<xs:enumeration value="CM7"/>
<xs:enumeration value="ARMV8MML"/>
<xs:enumeration value="ARMV8MBL"/>
<xs:enumeration value="ARMV81MML"/>
<xs:enumeration value="CA5"/>
<xs:enumeration value="CA7"/>
<xs:enumeration value="CA8"/>
<xs:enumeration value="CA9"/>
<xs:enumeration value="CA15"/>
<xs:enumeration value="CA17"/>
<xs:enumeration value="CA53"/>
<xs:enumeration value="CA57"/>
<xs:enumeration value="CA72"/>
<xs:enumeration value="other"/>
</xs:restriction>
</xs:simpleType>
<!-- revisionType specifies the CPU revision format as defined by ARM (rNpM) -->
<xs:simpleType name="revisionType">
<xs:restriction base="xs:string">
<xs:pattern value="r[0-9]*p[0-9]*"/>
</xs:restriction>
</xs:simpleType>
<!-- EndianType pre-defines the tokens for specifying the endianess of the device -->
<xs:simpleType name="endianType">
<xs:restriction base="xs:token">
<xs:enumeration value="little"/>
<xs:enumeration value="big"/>
<xs:enumeration value="selectable"/>
<xs:enumeration value="other"/>
</xs:restriction>
</xs:simpleType>
<!-- dataType pre-defines the tokens in line with CMSIS data type definitions -->
<xs:simpleType name="dataTypeType">
<xs:restriction base="xs:token">
<xs:enumeration value="uint8_t"/>
<xs:enumeration value="uint16_t"/>
<xs:enumeration value="uint32_t"/>
<xs:enumeration value="uint64_t"/>
<xs:enumeration value="int8_t"/>
<xs:enumeration value="int16_t"/>
<xs:enumeration value="int32_t"/>
<xs:enumeration value="int64_t"/>
<xs:enumeration value="uint8_t *"/>
<xs:enumeration value="uint16_t *"/>
<xs:enumeration value="uint32_t *"/>
<xs:enumeration value="uint64_t *"/>
<xs:enumeration value="int8_t *"/>
<xs:enumeration value="int16_t *"/>
<xs:enumeration value="int32_t *"/>
<xs:enumeration value="int64_t *"/>
</xs:restriction>
</xs:simpleType>
<!-- dimableIdentifierType specifies the subset and sequence of characters used for specifying identifiers that may contain %s from dim. -->
<!-- this is particularly important as these are used in ANSI C Structures during the device header file generation -->
<xs:simpleType name="dimableIdentifierType">
<xs:restriction base="xs:string">
<xs:pattern value="((%s)|(%s)[_A-Za-z]{1}[_A-Za-z0-9]*)|([_A-Za-z]{1}[_A-Za-z0-9]*(\[%s\])?)|([_A-Za-z]{1}[_A-Za-z0-9]*(%s)?[_A-Za-z0-9]*)"/>
</xs:restriction>
</xs:simpleType>
<!-- identifierType specifies the subset and sequence of characters used for specifying identifiers that must not contain %s from dim. -->
<!-- this is particularly important as these are used in ANSI C Structures during the device header file generation -->
<xs:simpleType name="identifierType">
<xs:restriction base="xs:string">
<xs:pattern value="[_A-Za-z0-9]*"/>
</xs:restriction>
</xs:simpleType>
<!-- V1.3: Protection Access Attribute Strings -->
<xs:simpleType name="protectionStringType">
<xs:restriction base="xs:string">
<!-- s = Secure -->
<!-- n = Non-secure -->
<!-- p = Privileged -->
<xs:pattern value="[snp]"/>
</xs:restriction>
</xs:simpleType>
<!-- V1.3: SAU Access Type -->
<xs:simpleType name="sauAccessType">
<xs:restriction base="xs:string">
<!-- c = non-secure Callable / Secure -->
<!-- n = Non-secure -->
<xs:pattern value="[cn]"/>
</xs:restriction>
</xs:simpleType>
<!-- dimIndexType specifies the subset and sequence of characters used for specifying the sequence of indices in register arrays -->
<xs:simpleType name="dimIndexType">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]+\-[0-9]+|[A-Z]-[A-Z]|[_0-9a-zA-Z]+(,\s*[_0-9a-zA-Z]+)+"/>
</xs:restriction>
</xs:simpleType>
<!-- scaledNonNegativeInteger specifies the format in which numbers are represented in hexadecimal or decimal format -->
<xs:simpleType name="scaledNonNegativeInteger">
<xs:restriction base="xs:string">
<xs:pattern value="[+]?(0x|0X|#)?[0-9a-fA-F]+[kmgtKMGT]?"/>
</xs:restriction>
</xs:simpleType>
<!-- enumeratedValueDataType specifies the number formats for the values in enumeratedValues -->
<xs:simpleType name="enumeratedValueDataType">
<xs:restriction base="xs:string">
<xs:pattern value="[+]?(((0x|0X)[0-9a-fA-F]+)|([0-9]+)|((#|0b)[01xX]+))"/>
</xs:restriction>
</xs:simpleType>
<!-- accessType specfies the pre-defined tokens for the available accesses -->
<xs:simpleType name="accessType">
<xs:restriction base="xs:token">
<xs:enumeration value="read-only"/>
<xs:enumeration value="write-only"/>
<xs:enumeration value="read-write"/>
<xs:enumeration value="writeOnce"/>
<xs:enumeration value="read-writeOnce"/>
</xs:restriction>
</xs:simpleType>
<!-- modifiedWriteValuesType specifies the pre-defined tokens for the write side effects -->
<xs:simpleType name="modifiedWriteValuesType">
<xs:restriction base="xs:token">
<xs:enumeration value="oneToClear"/>
<xs:enumeration value="oneToSet"/>
<xs:enumeration value="oneToToggle"/>
<xs:enumeration value="zeroToClear"/>
<xs:enumeration value="zeroToSet"/>
<xs:enumeration value="zeroToToggle"/>
<xs:enumeration value="clear"/>
<xs:enumeration value="set"/>
<xs:enumeration value="modify"/>
</xs:restriction>
</xs:simpleType>
<!-- readAction type specifies the pre-defined tokens for read side effects -->
<xs:simpleType name="readActionType">
<xs:restriction base="xs:token">
<xs:enumeration value="clear"/>
<xs:enumeration value="set"/>
<xs:enumeration value="modify"/>
<xs:enumeration value="modifyExternal"/>
</xs:restriction>
</xs:simpleType>
<!-- enumUsageType specifies the pre-defined tokens for selecting what access types an enumeratedValues set is associated with -->
<xs:simpleType name="enumUsageType">
<xs:restriction base="xs:token">
<xs:enumeration value="read"/>
<xs:enumeration value="write"/>
<xs:enumeration value="read-write"/>
</xs:restriction>
</xs:simpleType>
<!-- bitRangeType specifies the bit numbers to be restricted values from 0 - 69 -->
<xs:simpleType name="bitRangeType">
<xs:restriction base="xs:token">
<xs:pattern value="\[([0-4])?[0-9]:([0-4])?[0-9]\]"/>
</xs:restriction>
</xs:simpleType>
<!-- writeContraintType specifies how to describe the restriction of the allowed values that can be written to a resource -->
<xs:complexType name="writeConstraintType">
<xs:choice>
<xs:element name="writeAsRead" type="xs:boolean"/>
<xs:element name="useEnumeratedValues" type="xs:boolean"/>
<xs:element name="range">
<xs:complexType>
<xs:sequence>
<xs:element name="minimum" type="scaledNonNegativeInteger"/>
<xs:element name="maximum" type="scaledNonNegativeInteger"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<!-- addressBlockType specifies the elements to describe an address block -->
<xs:complexType name="addressBlockType">
<xs:sequence>
<xs:element name="offset" type="scaledNonNegativeInteger"/>
<xs:element name="size" type="scaledNonNegativeInteger"/>
<xs:element name="usage">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="registers"/>
<xs:enumeration value="buffer"/>
<xs:enumeration value="reserved"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<!-- Version 1.3.2: optional access protection for an address block s=secure n=non-secure p=privileged -->
<xs:element name="protection" type="protectionStringType" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<!-- interruptType specifies how to describe an interrupt associated with a peripheral -->
<xs:complexType name="interruptType">
<xs:sequence>
<xs:element name="name" type="stringType"/>
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="value" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
<!-- register properties group specifies register size, access permission and reset value
this is used in multiple locations. Settings are inherited downstream. -->
<xs:group name="registerPropertiesGroup">
<xs:sequence>
<xs:element name="size" type="scaledNonNegativeInteger" minOccurs="0"/>
<xs:element name="access" type="accessType" minOccurs="0"/>
<!-- V 1.3: extended register access protection -->
<xs:element name="protection" type="protectionStringType" minOccurs="0"/>
<xs:element name="resetValue" type="scaledNonNegativeInteger" minOccurs="0"/>
<xs:element name="resetMask" type="scaledNonNegativeInteger" minOccurs="0"/>
</xs:sequence>
</xs:group>
<!-- bitRangeLsbMsbStyle specifies the bit position of a field within a register
by specifying the least significant and the most significant bit position -->
<xs:group name="bitRangeLsbMsbStyle">
<xs:sequence>
<xs:element name="lsb" type="scaledNonNegativeInteger"/>
<xs:element name="msb" type="scaledNonNegativeInteger"/>
</xs:sequence>
</xs:group>
<!-- bitRangeOffsetWidthStyle specifies the bit position of a field within a register
by specifying the least significant bit position and the bitWidth of the field -->
<xs:group name="bitRangeOffsetWidthStyle">
<xs:sequence>
<xs:element name="bitOffset" type="scaledNonNegativeInteger"/>
<xs:element name="bitWidth" type="scaledNonNegativeInteger" minOccurs="0"/>
</xs:sequence>
</xs:group>
<!-- dimElementGroup specifies the number of array elements (dim), the address offset
between to consecutive array elements and an a comma seperated list of strings
being used for identifying each element in the array -->
<xs:group name="dimElementGroup">
<xs:sequence>
<xs:element name="dim" type="scaledNonNegativeInteger"/>
<xs:element name="dimIncrement" type="scaledNonNegativeInteger"/>
<xs:element name="dimIndex" type="dimIndexType" minOccurs="0"/>
<xs:element name="dimName" type="identifierType" minOccurs="0"/>
<xs:element name="dimArrayIndex" type="dimArrayIndexType" minOccurs="0"/>
</xs:sequence>
</xs:group>
<xs:complexType name="cpuType">
<xs:sequence>
<!-- V1.1: ARM processor name: Cortex-Mx / SCxxx -->
<xs:element name="name" type="cpuNameType"/>
<!-- V1.1: ARM defined revision of the cpu -->
<xs:element name="revision" type="revisionType"/>
<!-- V1.1: Endian specifies the endianess of the processor/device -->
<xs:element name="endian" type="endianType"/>
<!-- V1.1: mpuPresent specifies whether or not a memory protection unit is physically present -->
<xs:element name="mpuPresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.1: fpuPresent specifies whether or not a floating point hardware unit is physically present -->
<xs:element name="fpuPresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.2: fpuDP specifies a double precision floating point hardware unit is physically present-->
<xs:element name="fpuDP" type="xs:boolean" minOccurs="0"/>
<!-- V1.3: dspPresent specifies whether the optional SIMD instructions are supported by processor -->
<xs:element name="dspPresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.2: icachePresent specifies that an instruction cache is physically present-->
<xs:element name="icachePresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.2: dcachePresent specifies that a data cache is physically present-->
<xs:element name="dcachePresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.2: itcmPresent specifies that an instruction tightly coupled memory is physically present-->
<xs:element name="itcmPresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.2: dtcmPresent specifies that an data tightly coupled memory is physically present-->
<xs:element name="dtcmPresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.1: vtorPresent is used for Cortex-M0+ based devices only. It indicates whether the Vector -->
<!-- Table Offset Register is implemented in the device or not -->
<xs:element name="vtorPresent" type="xs:boolean" minOccurs="0"/>
<!-- V1.1: nvicPrioBits specifies the number of bits used by the Nested Vectored Interrupt Controller -->
<!-- for defining the priority level = # priority levels -->
<xs:element name="nvicPrioBits" type="scaledNonNegativeInteger"/>
<!-- V1.1: vendorSystickConfig is set true if a custom system timer is implemented in the device -->
<!-- instead of the ARM specified SysTickTimer -->
<xs:element name="vendorSystickConfig" type="xs:boolean"/>
<!-- V1.3: reports the total number of interrupts implemented by the device (optional) -->
<xs:element name="deviceNumInterrupts" type="scaledNonNegativeInteger" minOccurs="0"/>
<!-- V1.3: sauRegions specifies the available number of address regions -->
<!-- if not specified a value of zero is assumed -->
<xs:element name="sauNumRegions" type="scaledNonNegativeInteger" minOccurs="0"/>
<!-- V1.3: SAU Regions Configuration (if fully or partially predefined) -->
<xs:element name="sauRegionsConfig" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="region" minOccurs="0" maxOccurs="unbounded">
<!-- addressBlockType specifies the elements to describe an address block -->
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
<xs:element name="base" type="scaledNonNegativeInteger"/>
<xs:element name="limit" type="scaledNonNegativeInteger"/>
<xs:element name="access" type="sauAccessType"/>
</xs:sequence>
<xs:attribute name="enabled" type="xs:boolean" use="optional" default="true"/>
<xs:attribute name="name" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="enabled" type="xs:boolean" use="optional" default="true"/>
<xs:attribute name="protectionWhenDisabled" type="protectionStringType" use="optional" default="s"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="enumeratedValueType">
<xs:sequence>
<!-- name is a ANSI C indentifier representing the value (C Enumeration) -->
<xs:element name="name" type="identifierType"/>
<!-- description contains the details about the semantics/behavior specified by this value -->
<xs:element name="description" type="stringType" minOccurs="0"/>
<xs:choice>
<xs:element name="value" type="enumeratedValueDataType"/>
<!-- isDefault specifies the name and description for all values that are not
specifically described individually -->
<xs:element name="isDefault" type="xs:boolean"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="enumerationType">
<xs:sequence>
<!-- name specfies a reference to this enumeratedValues section for reuse purposes
this name does not appear in the System Viewer nor the Header File. -->
<xs:element name="name" type="identifierType" minOccurs="0"/>
<!-- overrides the hierarchical enumeration type in the device header file. User is responsible for uniqueness across description -->
<xs:element name="headerEnumName" type="identifierType" minOccurs="0"/>
<!-- usage specifies whether this enumeration is to be used for read or write or
(read and write) accesses -->
<xs:element name="usage" type="enumUsageType" minOccurs="0"/>
<!-- enumeratedValue derivedFrom=<identifierType> -->
<xs:element name="enumeratedValue" type="enumeratedValueType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="derivedFrom" type="identifierType" use="optional"/>
</xs:complexType>
<xs:complexType name="dimArrayIndexType">
<xs:sequence>
<xs:element name="headerEnumName" type="identifierType" minOccurs="0"/>
<xs:element name="enumeratedValue" type="enumeratedValueType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="fieldType">
<xs:sequence>
<xs:group ref="dimElementGroup" minOccurs="0"/>
<!-- name specifies a field's name. The System Viewer and the device header file will
use the name of the field as identifier -->
<xs:element name="name" type="dimableIdentifierType"/>
<!-- description contains reference manual level information about the function and
options of a field -->
<xs:element name="description" type="stringType" minOccurs="0"/>
<!-- alternative specifications of the bit position of the field within the register -->
<xs:choice minOccurs="1" maxOccurs="1">
<!-- bit field described by lsb followed by msb tag -->
<xs:group ref="bitRangeLsbMsbStyle"/>
<!-- bit field described by bit offset relative to Bit0 + bit width of field -->
<xs:group ref="bitRangeOffsetWidthStyle"/>
<!-- bit field described by [<msb>:<lsb>] -->
<xs:element name="bitRange" type="bitRangeType"/>
</xs:choice>
<!-- access describes the predefined permissions for the field. -->
<xs:element name="access" type="accessType" minOccurs="0"/>
<!-- predefined description of write side effects -->
<xs:element name="modifiedWriteValues" type="modifiedWriteValuesType" minOccurs="0"/>
<!-- writeContstraint specifies the subrange of allowed values -->
<xs:element name="writeConstraint" type="writeConstraintType" minOccurs="0"/>
<!-- readAction specifies the read side effects. -->
<xs:element name="readAction" type="readActionType" minOccurs="0"/>
<!-- enumeratedValues derivedFrom=<identifierType> -->
<xs:element name="enumeratedValues" type="enumerationType" minOccurs="0" maxOccurs="2">
</xs:element>
</xs:sequence>
<xs:attribute name="derivedFrom" type="dimableIdentifierType" use="optional"/>
</xs:complexType>
<xs:complexType name="fieldsType">
<xs:sequence>
<!-- field derivedFrom=<identifierType> -->
<xs:element name="field" type="fieldType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="registerType">
<xs:sequence>
<xs:group ref="dimElementGroup" minOccurs="0"/>
<!-- name specifies the name of the register. The register name is used by System Viewer and
device header file generator to represent a register -->
<xs:element name="name" type="dimableIdentifierType"/>
<!-- display name specifies a register name without the restritions of an ANSIS C identifier.
The use of this tag is discouraged because it does not allow consistency between
the System View and the device header file. -->
<xs:element name="displayName" type="stringType" minOccurs="0"/>
<!-- description contains a reference manual level description about the register and it's purpose -->
<xs:element name="description" type="stringType" minOccurs="0"/>
<xs:choice>
<!-- alternateGroup specifies the identifier of the subgroup a register belongs to.
This is useful if a register has a different description per mode but a single name -->
<xs:element name="alternateGroup" type="identifierType" minOccurs="0"/>
<!-- V1.1: alternateRegister specifies an alternate register description for an address that is
already fully described. In this case the register name must be unique within the peripheral -->
<xs:element name="alternateRegister" type="dimableIdentifierType" minOccurs="0"/>
</xs:choice>
<!-- addressOffset describes the address of the register relative to the baseOffset of the peripheral -->
<xs:element name="addressOffset" type="scaledNonNegativeInteger"/>
<!-- registerPropertiesGroup elements specify the default values for register size, access permission and
reset value. These default values are inherited to all fields contained in this register -->
<xs:group ref="registerPropertiesGroup" minOccurs="0"/>
<!-- V1.1: dataType specifies a CMSIS compliant native dataType for a register (i.e. signed, unsigned, pointer) -->
<xs:element name="dataType" type="dataTypeType" minOccurs="0"/>
<!-- modifiedWriteValues specifies the write side effects -->
<xs:element name="modifiedWriteValues" type="modifiedWriteValuesType" minOccurs="0"/>
<!-- writeConstraint specifies the subset of allowed write values -->
<xs:element name="writeConstraint" type="writeConstraintType" minOccurs="0"/>
<!-- readAcction specifies the read side effects -->
<xs:element name="readAction" type="readActionType" minOccurs="0"/>
<!-- fields section contains all fields that belong to this register -->
<xs:element name="fields" type="fieldsType" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="derivedFrom" type="dimableIdentifierType" use="optional"/>
</xs:complexType>
<!-- V1.1: A cluster is a set of registers that are composed into a C data structure in the device header file -->
<xs:complexType name="clusterType">
<xs:sequence>
<xs:group ref="dimElementGroup" minOccurs="0"/>
<xs:element name="name" type="dimableIdentifierType"/>
<xs:element name="description" type="xs:string"/>
<!-- V1.1: alternateCluster specifies an alternative description for a cluster address range that is
already fully described. In this case the cluster name must be unique within the peripheral -->
<xs:element name="alternateCluster" type="dimableIdentifierType" minOccurs="0"/>
<!-- V1.1: headerStructName specifies the name for the cluster structure typedef
used in the device header generation instead of the cluster name -->
<xs:element name="headerStructName" type="identifierType" minOccurs="0"/>
<xs:element name="addressOffset" type="scaledNonNegativeInteger"/>
<!-- registerPropertiesGroup elements specify the default values for register size, access permission and
reset value. These default values are inherited to all registers contained in this peripheral -->
<xs:group ref="registerPropertiesGroup" minOccurs="0"/>
<xs:sequence>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="register" type="registerType" minOccurs="0" maxOccurs="unbounded"/>
<!-- 1.3: nesting of cluster is supported -->
<xs:element name="cluster" type="clusterType" minOccurs="0" maxOccurs="unbounded"/>
</xs:choice>
</xs:sequence>
</xs:sequence>
<xs:attribute name="derivedFrom" type="dimableIdentifierType" use="optional"/>
</xs:complexType>
<!-- the registers section can have an arbitrary list of cluster and register sections -->
<xs:complexType name="registersType">
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="cluster" type="clusterType"/>
<xs:element name="register" type="registerType"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="peripheralType">
<xs:sequence>
<!-- 1.3: specify uni-dimensional array of peripheral - requires name="<name>[%s]" -->
<xs:group ref="dimElementGroup" minOccurs="0"/>
<!-- name specifies the name of a peripheral. This name is used for the System View and device header file -->
<xs:element name="name" type="dimableIdentifierType"/>
<!-- version specifies the version of the peripheral descriptions -->
<xs:element name="version" type="stringType" minOccurs="0"/>
<!-- description provides a high level functional description of the peripheral -->
<xs:element name="description" type="stringType" minOccurs="0"/>
<!-- V1.1: alternatePeripheral specifies an alternative description for an address range that is
already fully by a peripheral described. In this case the peripheral name must be unique within the device description -->
<xs:element name="alternatePeripheral" type="dimableIdentifierType" minOccurs="0"/>
<!-- groupName assigns this peripheral to a group of peripherals. This is only used bye the System View -->
<xs:element name="groupName" type="xs:Name" minOccurs="0"/>
<!-- prependToName specifies a prefix that is placed in front of each register name of this peripheral.
The device header file will show the registers in a C-Struct of the peripheral without the prefix. -->
<xs:element name="prependToName" type="identifierType" minOccurs="0"/>
<!-- appendToName is a postfix that is appended to each register name of this peripheral. The device header
file will sho the registers in a C-Struct of the peripheral without the postfix -->
<xs:element name="appendToName" type="identifierType" minOccurs="0"/>
<!-- V1.1: headerStructName specifies the name for the peripheral structure typedef
used in the device header generation instead of the peripheral name -->
<xs:element name="headerStructName" type="dimableIdentifierType" minOccurs="0"/>
<!-- disableCondition contains a logical expression based on constants and register or bit-field values
if the condition is evaluated to true, the peripheral display will be disabled -->
<xs:element name="disableCondition" type="stringType" minOccurs="0"/>
<!-- baseAddress specifies the absolute base address of a peripheral. For derived peripherals it is mandatory
to specify a baseAddress. -->
<xs:element name="baseAddress" type="scaledNonNegativeInteger"/>
<!-- registerPropertiesGroup elements specify the default values for register size, access permission and
reset value. These default values are inherited to all registers contained in this peripheral -->
<xs:group ref="registerPropertiesGroup" minOccurs="0"/>
<!-- addressBlock specifies one or more address ranges that are assigned exclusively to this peripheral.
derived peripherals may have no addressBlock, however none-derived peripherals are required to specify
at least one address block -->
<xs:element name="addressBlock" type="addressBlockType" minOccurs="0" maxOccurs="unbounded"/>
<!-- interrupt specifies can specify one or more interrtupts by name, description and value -->
<xs:element name="interrupt" type="interruptType" minOccurs="0" maxOccurs="unbounded"/>
<!-- registers section contains all registers owned by the peripheral. In case a peripheral gets derived it does
not have its own registers section, hence this section is optional. A unique peripheral without a
registers section is not allowed -->
<xs:element name="registers" type="registersType" minOccurs="0" maxOccurs="1">
</xs:element>
</xs:sequence>
<xs:attribute name="derivedFrom" type="dimableIdentifierType" use="optional"/>
</xs:complexType>
<!-- ==================================================== -->
<!-- The top level element of a description is the device -->
<!-- ==================================================== -->
<xs:element name="device" nillable="true">
<xs:complexType>
<xs:sequence>
<!-- V1.1: Vendor Name -->
<xs:element name="vendor" type="stringType" minOccurs="0"/>
<!-- V1.1: Vendor ID - a short name for referring to the vendor (e.g. Texas Instruments = TI) -->
<xs:element name="vendorID" type="identifierType" minOccurs="0"/>
<!-- name specifies the device name being described -->
<xs:element name="name" type="identifierType"/>
<!-- V1.1: series specifies the device series or family name -->
<xs:element name="series" type="stringType" minOccurs="0"/>
<!-- version specifies the version of the device description -->
<xs:element name="version" type="stringType"/>
<!-- description is a string describing the device features (e.g. memory size, peripherals, etc.) -->
<xs:element name="description" type="stringType"/>
<!-- V1.1: licenseText specifies the file header section to be included in any derived file -->
<xs:element name="licenseText" type="stringType" minOccurs="0"/>
<!-- V1.1: cpu specifies the details of the processor included in the device -->
<xs:element name="cpu" type="cpuType" minOccurs="0"/>
<!-- V1.1: the tag specifies the filename without extension of the CMSIS System Device include file.
This tag is used by the header file generator for customizing the include statement referencing the
CMSIS system file within the CMSIS device header file. By default the filename is "system_<device.name>"
In cases a device series shares a single system header file, the name of the series shall be used
instead of the individual device name. -->
<xs:element name="headerSystemFilename" type="identifierType" minOccurs="0"/>
<!-- V1.1: headerDefinitionPrefix specifies the string being prepended to all names of types defined in
generated device header file -->
<xs:element name="headerDefinitionsPrefix" type="identifierType" minOccurs="0"/>
<!-- addressUnitBits specifies the size of the minimal addressable unit in bits -->
<xs:element name="addressUnitBits" type="scaledNonNegativeInteger"/>
<!-- width specifies the number of bits for the maximum single transfer size allowed by the bus interface.
This sets the maximum size of a single register that can be defined for an address space -->
<xs:element name="width" type="scaledNonNegativeInteger"/>
<!-- registerPropertiesGroup elements specify the default values for register size, access permission and
reset value -->
<xs:group ref="registerPropertiesGroup" minOccurs="0"/>
<!-- peripherals is containing all peripherals -->
<xs:element name="peripherals">
<xs:complexType>
<xs:sequence>
<xs:element name="peripheral" type="peripheralType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Vendor Extensions: this section captures custom extensions. This section will be ignored by default -->
<xs:element name="vendorExtensions" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded">
</xs:any>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="schemaVersion" type="xs:decimal" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
<!-- END OF FILE -->

@ -0,0 +1,144 @@
const std = @import("std");
const clap = @import("clap");
const xml = @import("xml.zig");
const svd = @import("svd.zig");
const Database = @import("Database.zig");
const ArenaAllocator = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
pub const log_level: std.log.Level = .info;
const svd_schema = @embedFile("cmsis-svd.xsd");
const params = [_]clap.Param(clap.Help){
clap.parseParam("-h, --help Display this help and exit") catch unreachable,
clap.parseParam("-s, --schema <TYPE> Explicitly set schema type, one of: svd, atdf, json") catch unreachable,
clap.parseParam("<POS>...") catch unreachable,
};
pub fn main() !void {
mainImpl() catch |err| switch (err) {
error.Explained => std.process.exit(1),
else => return err,
};
}
const Schema = enum {
atdf,
dslite,
json,
svd,
xml,
};
fn mainImpl() anyerror!void {
defer xml.cleanupParser();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer _ = gpa.deinit();
var diag = clap.Diagnostic{};
var args = clap.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| {
// Report useful error and exit
diag.report(std.io.getStdErr().writer(), err) catch {};
return error.Explained;
};
defer args.deinit();
if (args.flag("--help"))
return clap.help(std.io.getStdErr().writer(), &params);
var schema: ?Schema = if (args.option("--schema")) |schema_str|
if (std.meta.stringToEnum(Schema, schema_str)) |s| s else {
std.log.err("Unknown schema type: {s}, must be one of: svd, atdf, json", .{schema_str});
return error.Explained;
}
else
null;
const positionals = args.positionals();
var db = switch (positionals.len) {
0 => blk: {
if (schema == null) {
std.log.err("schema must be chosen when reading from stdin", .{});
return error.Explained;
}
if (schema.? == .json) {
return error.Todo;
}
var stdin = std.io.getStdIn().reader();
const doc: *xml.Doc = xml.readIo(readFn, null, &stdin, null, null, 0) orelse return error.ReadXmlFd;
defer xml.freeDoc(doc);
break :blk try parseXmlDatabase(allocator, doc, schema.?);
},
1 => blk: {
// if schema is null, then try to determine using file extension
if (schema == null) {
const ext = std.fs.path.extension(positionals[0]);
if (ext.len > 0) {
schema = std.meta.stringToEnum(Schema, ext[1..]) orelse {
std.log.err("unable to determine schema from file extension of '{s}'", .{positionals[0]});
return error.Explained;
};
}
}
// schema is guaranteed to be non-null from this point on
if (schema.? == .json) {
return error.Todo;
}
// all other schema types are xml based
const doc: *xml.Doc = xml.readFile(positionals[0].ptr, null, 0) orelse return error.ReadXmlFile;
defer xml.freeDoc(doc);
break :blk try parseXmlDatabase(allocator, doc, schema.?);
},
else => {
std.log.err("this program takes max one positional argument for now", .{});
return error.Explained;
},
};
defer db.deinit();
var buffered = std.io.bufferedWriter(std.io.getStdOut().writer());
try db.toZig(std.io.getStdOut().writer()); //buffered.writer());
try buffered.flush();
}
fn readFn(ctx: ?*anyopaque, buffer: ?[*]u8, len: c_int) callconv(.C) c_int {
if (buffer == null)
return -1;
return if (ctx) |c| blk: {
const reader = @ptrCast(*std.fs.File.Reader, @alignCast(@alignOf(*std.fs.File.Reader), c));
const n = reader.read(buffer.?[0..@intCast(usize, len)]) catch return -1;
break :blk @intCast(c_int, n);
} else -1;
}
fn parseXmlDatabase(allocator: std.mem.Allocator, doc: *xml.Doc, schema: Schema) !Database {
return switch (schema) {
.json => unreachable,
.atdf => try Database.initFromAtdf(allocator, doc),
.svd => try Database.initFromSvd(allocator, doc),
.dslite => return error.Todo,
.xml => determine_type: {
const root_element: *xml.Node = xml.docGetRootElement(doc) orelse return error.NoRoot;
if (xml.findValueForKey(root_element, "device") != null)
break :determine_type try Database.initFromSvd(allocator, doc)
else if (xml.findValueForKey(root_element, "avr-tools-device-file") != null)
break :determine_type try Database.initFromAtdf(allocator, doc)
else {
std.log.err("unable do detect register schema type", .{});
return error.Explained;
}
},
};
}

@ -0,0 +1,87 @@
const std = @import("std");
pub fn mmio(addr: usize, comptime size: u8, comptime PackedT: type) *volatile Mmio(size, PackedT) {
return @intToPtr(*volatile Mmio(size, PackedT), addr);
}
pub fn Mmio(comptime size: u8, comptime PackedT: type) type {
if ((size % 8) != 0)
@compileError("size must be divisible by 8!");
if (!std.math.isPowerOfTwo(size / 8))
@compileError("size must encode a power of two number of bytes!");
const IntT = std.meta.Int(.unsigned, size);
if (@sizeOf(PackedT) != (size / 8))
@compileError(std.fmt.comptimePrint("IntT and PackedT must have the same size!, they are {} and {} bytes respectively", .{ size / 8, @sizeOf(PackedT) }));
return extern struct {
const Self = @This();
raw: IntT,
pub const underlying_type = PackedT;
pub fn read(addr: *volatile Self) PackedT {
return @bitCast(PackedT, addr.raw);
}
pub fn write(addr: *volatile Self, val: PackedT) void {
// This is a workaround for a compiler bug related to miscompilation
// If the tmp var is not used, result location will fuck things up
var tmp = @bitCast(IntT, val);
addr.raw = tmp;
}
pub fn modify(addr: *volatile Self, fields: anytype) void {
var val = read(addr);
inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
@field(val, field.name) = @field(fields, field.name);
}
write(addr, val);
}
pub fn toggle(addr: *volatile Self, fields: anytype) void {
var val = read(addr);
inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
@field(val, @tagName(field.default_value.?)) = !@field(val, @tagName(field.default_value.?));
}
write(addr, val);
}
};
}
pub fn MmioInt(comptime size: u8, comptime T: type) type {
return extern struct {
const Self = @This();
raw: std.meta.Int(.unsigned, size),
pub fn read(addr: *volatile Self) T {
return @truncate(T, addr.raw);
}
pub fn modify(addr: *volatile Self, val: T) void {
const Int = std.meta.Int(.unsigned, size);
const mask = ~@as(Int, (1 << @bitSizeOf(T)) - 1);
var tmp = addr.raw;
addr.raw = (tmp & mask) | val;
}
};
}
pub fn mmioInt(addr: usize, comptime size: usize, comptime T: type) *volatile MmioInt(size, T) {
return @intToPtr(*volatile MmioInt(size, T), addr);
}
const InterruptVector = extern union {
C: fn () callconv(.C) void,
Naked: fn () callconv(.Naked) void,
// Interrupt is not supported on arm
};
fn unhandled() callconv(.C) noreturn {
@panic("unhandled interrupt");
}

@ -0,0 +1,416 @@
const std = @import("std");
const xml = @import("xml.zig");
const ArenaAllocator = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator;
// TODO: normalize descriptions, watch out for explicit '\n's tho, we want to replicate those newlines in generated text
pub const Device = struct {
vendor: ?[]const u8,
vendor_id: ?[]const u8,
name: ?[]const u8,
series: ?[]const u8,
version: ?[]const u8,
description: ?[]const u8,
license_text: ?[]const u8,
address_unit_bits: usize,
width: usize,
register_properties: struct {
size: ?usize,
access: ?Access,
protection: ?[]const u8,
reset_value: ?[]const u8,
reset_mask: ?[]const u8,
},
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Device {
const allocator = arena.allocator();
return Device{
.vendor = if (xml.findValueForKey(nodes, "vendor")) |str| try allocator.dupe(u8, str) else null,
.vendor_id = if (xml.findValueForKey(nodes, "vendorID")) |str| try allocator.dupe(u8, str) else null,
.name = if (xml.findValueForKey(nodes, "name")) |name| try allocator.dupe(u8, name) else null,
.series = if (xml.findValueForKey(nodes, "series")) |str| try allocator.dupe(u8, str) else null,
.version = if (xml.findValueForKey(nodes, "version")) |str| try allocator.dupe(u8, str) else null,
.description = if (xml.findValueForKey(nodes, "description")) |str| try allocator.dupe(u8, str) else null,
.license_text = if (xml.findValueForKey(nodes, "licenseText")) |str| try allocator.dupe(u8, str) else null,
.address_unit_bits = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "addressUnitBits") orelse return error.NoAddressUnitBits, 0),
.width = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "width") orelse return error.NoDeviceWidth, 0),
.register_properties = .{
// register properties group
.size = if (xml.findValueForKey(nodes, "size")) |size_str|
try std.fmt.parseInt(usize, size_str, 0)
else
null,
.access = if (xml.findValueForKey(nodes, "access")) |access_str|
try Access.parse(access_str)
else
null,
.protection = if (xml.findValueForKey(nodes, "protection")) |str| try allocator.dupe(u8, str) else null,
.reset_value = if (xml.findValueForKey(nodes, "resetValue")) |str| try allocator.dupe(u8, str) else null,
.reset_mask = if (xml.findValueForKey(nodes, "resetMask")) |str| try allocator.dupe(u8, str) else null,
},
};
}
};
pub const CpuName = enum {
cortex_m0,
cortex_m0plus,
cortex_m1,
sc000, // kindof like an m3
cortex_m23,
cortex_m3,
cortex_m33,
cortex_m35p,
cortex_m55,
sc300,
cortex_m4,
cortex_m7,
arm_v8_mml,
arm_v8_mbl,
arm_v81_mml,
cortex_a5,
cortex_a7,
cortex_a8,
cortex_a9,
cortex_a15,
cortex_a17,
cortex_a53,
cortex_a57,
cortex_a72,
other,
// TODO: finish
pub fn parse(str: []const u8) ?CpuName {
return if (std.mem.eql(u8, "CM0", str))
CpuName.cortex_m0
else if (std.mem.eql(u8, "CM0PLUS", str))
CpuName.cortex_m0plus
else if (std.mem.eql(u8, "CM0+", str))
CpuName.cortex_m0plus
else if (std.mem.eql(u8, "CM1", str))
CpuName.cortex_m1
else if (std.mem.eql(u8, "SC000", str))
CpuName.sc000
else if (std.mem.eql(u8, "CM23", str))
CpuName.cortex_m23
else if (std.mem.eql(u8, "CM3", str))
CpuName.cortex_m3
else if (std.mem.eql(u8, "CM33", str))
CpuName.cortex_m33
else if (std.mem.eql(u8, "CM35P", str))
CpuName.cortex_m35p
else if (std.mem.eql(u8, "CM55", str))
CpuName.cortex_m55
else if (std.mem.eql(u8, "SC300", str))
CpuName.sc300
else if (std.mem.eql(u8, "CM4", str))
CpuName.cortex_m4
else if (std.mem.eql(u8, "CM7", str))
CpuName.cortex_m7
else
null;
}
};
pub const Endian = enum {
little,
big,
selectable,
other,
pub fn parse(str: []const u8) !Endian {
return if (std.meta.stringToEnum(Endian, str)) |val|
val
else
error.UnknownEndianType;
}
};
pub const Cpu = struct {
//name: ?CpuName,
name: ?[]const u8,
revision: []const u8,
endian: Endian,
//mpu_present: bool,
//fpu_present: bool,
//fpu_dp: bool,
//dsp_present: bool,
//icache_present: bool,
//dcache_present: bool,
//itcm_present: bool,
//dtcm_present: bool,
//vtor_present: bool,
nvic_prio_bits: usize,
vendor_systick_config: bool,
device_num_interrupts: ?usize,
//sau_num_regions: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Cpu {
return Cpu{
.name = if (xml.findValueForKey(nodes, "name")) |name| try arena.allocator().dupe(u8, name) else null,
.revision = xml.findValueForKey(nodes, "revision") orelse unreachable,
.endian = try Endian.parse(xml.findValueForKey(nodes, "endian") orelse unreachable),
.nvic_prio_bits = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "nvicPrioBits") orelse unreachable, 0),
// TODO: booleans
.vendor_systick_config = (try xml.parseBoolean(arena.child_allocator, nodes, "vendorSystickConfig")) orelse false,
.device_num_interrupts = if (xml.findValueForKey(nodes, "deviceNumInterrupts")) |size_str|
try std.fmt.parseInt(usize, size_str, 0)
else
null,
};
}
};
pub const Access = enum {
read_only,
write_only,
read_write,
writeonce,
read_writeonce,
pub fn parse(str: []const u8) !Access {
return if (std.mem.eql(u8, "read-only", str))
Access.read_only
else if (std.mem.eql(u8, "write-only", str))
Access.write_only
else if (std.mem.eql(u8, "read-write", str))
Access.read_write
else if (std.mem.eql(u8, "writeOnce", str))
Access.writeonce
else if (std.mem.eql(u8, "read-writeOnce", str))
Access.read_writeonce
else
error.UnknownAccessType;
}
};
pub const Peripheral = struct {
name: []const u8,
version: ?[]const u8,
description: ?[]const u8,
base_addr: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Peripheral {
const allocator = arena.allocator();
return Peripheral{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
.version = if (xml.findValueForKey(nodes, "version")) |version|
try allocator.dupe(u8, version)
else
null,
.description = try xml.parseDescription(allocator, nodes, "description"),
.base_addr = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "baseAddress")) orelse return error.NoBaseAddr, // isDefault?
};
}
};
pub const Interrupt = struct {
name: []const u8,
description: ?[]const u8,
value: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Interrupt {
const allocator = arena.allocator();
return Interrupt{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
.description = try xml.parseDescription(allocator, nodes, "description"),
.value = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "value") orelse return error.NoValue, 0),
};
}
pub fn lessThan(_: void, lhs: Interrupt, rhs: Interrupt) bool {
return lhs.value < rhs.value;
}
pub fn compare(_: void, lhs: Interrupt, rhs: Interrupt) std.math.Order {
return if (lhs.value < rhs.value)
std.math.Order.lt
else if (lhs.value == rhs.value)
std.math.Order.eq
else
std.math.Order.gt;
}
};
pub const Register = struct {
name: []const u8,
description: ?[]const u8,
addr_offset: usize,
size: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node, device_width: usize) !Register {
const allocator = arena.allocator();
return Register{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
.description = try xml.parseDescription(allocator, nodes, "description"),
.addr_offset = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "addressOffset") orelse return error.NoAddrOffset, 0),
.size = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "size")) orelse device_width,
};
}
};
pub const Cluster = struct {
name: []const u8,
description: ?[]const u8,
addr_offset: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Cluster {
const allocator = arena.allocator();
return Cluster{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
.description = try xml.parseDescription(allocator, nodes, "description"),
.addr_offset = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "addressOffset") orelse return error.NoAddrOffset, 0),
};
}
};
const BitRange = struct {
offset: u8,
width: u8,
};
pub const Field = struct {
name: []const u8,
description: ?[]const u8,
offset: u8,
width: u8,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Field {
const allocator = arena.allocator();
// TODO:
const bit_range = blk: {
const lsb_opt = xml.findValueForKey(nodes, "lsb");
const msb_opt = xml.findValueForKey(nodes, "msb");
if (lsb_opt != null and msb_opt != null) {
const lsb = try std.fmt.parseInt(u8, lsb_opt.?, 0);
const msb = try std.fmt.parseInt(u8, msb_opt.?, 0);
if (msb < lsb)
return error.InvalidRange;
break :blk BitRange{
.offset = lsb,
.width = msb - lsb + 1,
};
}
const bit_offset_opt = xml.findValueForKey(nodes, "bitOffset");
const bit_width_opt = xml.findValueForKey(nodes, "bitWidth");
if (bit_offset_opt != null and bit_width_opt != null) {
const offset = try std.fmt.parseInt(u8, bit_offset_opt.?, 0);
const width = try std.fmt.parseInt(u8, bit_width_opt.?, 0);
break :blk BitRange{
.offset = offset,
.width = width,
};
}
const bit_range_opt = xml.findValueForKey(nodes, "bitRange");
if (bit_range_opt) |bit_range_str| {
var it = std.mem.tokenize(u8, bit_range_str, "[:]");
const msb = try std.fmt.parseInt(u8, it.next() orelse return error.NoMsb, 0);
const lsb = try std.fmt.parseInt(u8, it.next() orelse return error.NoLsb, 0);
if (msb < lsb)
return error.InvalidRange;
break :blk BitRange{
.offset = lsb,
.width = msb - lsb + 1,
};
}
return error.InvalidRange;
};
return Field{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
.offset = bit_range.offset,
.width = bit_range.width,
.description = try xml.parseDescription(allocator, nodes, "description"),
};
}
pub fn lessThan(_: void, lhs: Field, rhs: Field) bool {
return if (lhs.offset == rhs.offset)
lhs.width < rhs.width
else
lhs.offset < rhs.offset;
}
};
pub const EnumeratedValue = struct {
name: []const u8,
description: ?[]const u8,
value: ?usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !EnumeratedValue {
const allocator = arena.allocator();
return EnumeratedValue{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
.description = try xml.parseDescription(allocator, nodes, "description"),
.value = try xml.parseIntForKey(usize, arena.child_allocator, nodes, "value"), // TODO: isDefault?
};
}
};
pub const Dimension = struct {
dim: usize,
increment: usize,
/// a range of 0-index, only index is recorded
index: ?Index,
name: ?[]const u8,
//array_index: ,
const Index = union(enum) {
num: usize,
list: std.ArrayList([]const u8),
};
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !?Dimension {
const allocator = arena.allocator();
return Dimension{
.dim = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "dim")) orelse return null,
.increment = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "dimIncrement")) orelse return null,
.index = if (xml.findValueForKey(nodes, "dimIndex")) |index_str|
if (std.mem.indexOf(u8, index_str, ",") != null) blk: {
var list = std.ArrayList([]const u8).init(allocator);
var it = std.mem.tokenize(u8, index_str, ",");
var expected: usize = 0;
while (it.next()) |token| : (expected += 1)
try list.append(try allocator.dupe(u8, token));
break :blk Index{
.list = list,
};
} else blk: {
var it = std.mem.tokenize(u8, index_str, "-");
const begin = try std.fmt.parseInt(usize, it.next() orelse return error.InvalidDimIndex, 10);
const end = try std.fmt.parseInt(usize, it.next() orelse return error.InvalidDimIndex, 10);
if (begin == 0)
break :blk Index{
.num = end + 1,
};
var list = std.ArrayList([]const u8).init(allocator);
var i = begin;
while (i <= end) : (i += 1)
try list.append(try std.fmt.allocPrint(allocator, "{}", .{i}));
break :blk Index{
.list = list,
};
}
else
null,
.name = if (xml.findValueForKey(nodes, "dimName")) |name_str|
try allocator.dupe(u8, name_str)
else
null,
};
}
};

@ -0,0 +1,107 @@
const std = @import("std");
const c = @cImport({
@cDefine("LIBXML_TREE_ENABLED", {});
@cDefine("LIBXML_SCHEMAS_ENABLED", {});
@cDefine("LIBXML_READER_ENABLED", {});
@cInclude("libxml/xmlreader.h");
});
const Allocator = std.mem.Allocator;
pub const Node = c.xmlNode;
pub const Doc = c.xmlDoc;
pub const readFile = c.xmlReadFile;
pub const readIo = c.xmlReadIO;
pub const cleanupParser = c.xmlCleanupParser;
pub const freeDoc = c.xmlFreeDoc;
pub const docGetRootElement = c.xmlDocGetRootElement;
pub fn getAttribute(node: ?*Node, key: [:0]const u8) ?[]const u8 {
if (c.xmlHasProp(node, key.ptr)) |prop| {
if (@ptrCast(*c.xmlAttr, prop).children) |value_node| {
if (@ptrCast(*Node, value_node).content) |content| {
return std.mem.span(content);
}
}
}
return null;
}
pub fn findNode(node: ?*Node, key: []const u8) ?*Node {
return if (node) |n| blk: {
var it: ?*Node = n;
break :blk while (it != null) : (it = it.?.next) {
if (it.?.type != 1)
continue;
const name = std.mem.span(it.?.name orelse continue);
if (std.mem.eql(u8, key, name))
break it;
} else null;
} else null;
}
pub fn findValueForKey(node: ?*Node, key: []const u8) ?[]const u8 {
return if (findNode(node, key)) |n|
if (@ptrCast(?*Node, n.children)) |child|
if (@ptrCast(?[*:0]const u8, child.content)) |content|
std.mem.span(content)
else
null
else
null
else
null;
}
pub fn parseDescription(allocator: Allocator, node: ?*Node, key: []const u8) !?[]const u8 {
return if (findValueForKey(node, key)) |value| blk: {
var str = std.ArrayList(u8).init(allocator);
errdefer str.deinit();
var it = std.mem.tokenize(u8, value, " \n\t\r");
try str.appendSlice(it.next() orelse return null);
while (it.next()) |token| {
try str.append(' ');
try str.appendSlice(token);
}
break :blk str.toOwnedSlice();
} else null;
}
pub fn parseIntForKey(comptime T: type, allocator: std.mem.Allocator, node: ?*Node, key: []const u8) !?T {
return if (findValueForKey(node, key)) |str| blk: {
const lower = try std.ascii.allocLowerString(allocator, str);
defer allocator.free(lower);
break :blk if (std.mem.startsWith(u8, lower, "#")) weird_base2: {
for (lower[1..]) |*character| {
if (character.* == 'x') {
character.* = '0';
}
}
break :weird_base2 try std.fmt.parseInt(T, lower[1..], 2);
} else try std.fmt.parseInt(T, lower, 0);
} else null;
}
pub fn parseBoolean(allocator: Allocator, node: ?*Node, key: []const u8) !?bool {
return if (findValueForKey(node, key)) |str| blk: {
const lower = try std.ascii.allocLowerString(allocator, str);
defer allocator.free(lower);
break :blk if (std.mem.eql(u8, "0", lower))
false
else if (std.mem.eql(u8, "1", lower))
true
else if (std.mem.eql(u8, "false", lower))
false
else if (std.mem.eql(u8, "true", lower))
true
else
return error.InvalidBoolean;
} else null;
}
Loading…
Cancel
Save