224 lines
8.8 KiB
Plaintext
224 lines
8.8 KiB
Plaintext
|
|
* Constants can't be bigger than 32 bits. For example:
|
|
constants mcg_ctl_val "Global MC control values" {
|
|
mc_enable = 0xffffffffffffffff;
|
|
mc_disable = 0x0;
|
|
};
|
|
- again.
|
|
|
|
* Required additional checks are now:
|
|
- Constant values all fit into the bit width specified
|
|
|
|
* How will we do device inheritance? Clearly the new device is a
|
|
new type, but it also needs a new operation for every register,
|
|
even those inherited from the base device type. This suggests that
|
|
register definitions should always be copied from the base type,
|
|
but register type definitions (and constant declarations) should
|
|
simply be referenced.
|
|
|
|
New syntax for inheritance:
|
|
1) extends(xapic) as an extra device attribute.
|
|
2) override register foo ... to override a register definition.
|
|
3) elide register bar; removes defn of bar from the new device.
|
|
|
|
* Generalize register field attributes. Specify fixed values
|
|
properly, as either a literal, or a constant value name, or
|
|
(possibly) a compile-time expression. Split up whether the driver
|
|
can read or write the field, and in what way, from what
|
|
consequences there are of reading or writing the field.
|
|
|
|
What is the difference between the read cycles permissible by the
|
|
hardware, and the read operations that the user might want to be able
|
|
to initiate?
|
|
|
|
Well, whether the hardware allows reads or not is simply a bit.
|
|
Hardware-readable: boolean. False => never generate a read
|
|
cycle. This is a property of a register, not a field!
|
|
|
|
What reads the user is allowed to perform is always one of:
|
|
R : 'read_value' : Return any value to user (expose a read operation)
|
|
RF: 'read_as' Always same constant value (expose sanity check to drivers)
|
|
RC: 'read_clears' Reads as some value, and also clears.
|
|
RN: 'no_read' Can't be read.
|
|
These are properties of fields; register properties are templates
|
|
for these.
|
|
|
|
Can you allow a hardware write or not?
|
|
If you can, what are the constraints on this? These are field
|
|
properties, but might apply to the register as a whole.
|
|
|
|
Options for writing:
|
|
W : 'write_value' Set to any value of a type (expose a write operation)
|
|
WN: 'no_write' Cannot be written/writes ignored (not exposed to user)
|
|
WF: 'write_as' Must write a specific value (not exposed to user)
|
|
WP: 'preserve' Must preserve previous value (not exposed to user)
|
|
WC: 'write_to_clear(v)' Must write a specific value to clear register
|
|
WS: 'write_to_set(v)' Must write a specific value to set register
|
|
|
|
Options for other actions:
|
|
sticky: once written, value will never change.
|
|
|
|
Shorthand:
|
|
rw => read_value write_value
|
|
ro => readable no_write
|
|
wo => no_read writeable
|
|
always(v) => read_as(v) write_as(v)
|
|
mbz => no_read write_as(0)
|
|
mb1 => no_read write_as(1s)
|
|
rsvd => no_read preserve
|
|
ros => readable no_write sticky
|
|
rwc => readable writeable write_to_clear(1s)
|
|
rwzc => readable writeable write_to_clear(0)
|
|
rws => readable writeable sticky
|
|
|
|
Functions to generate for a field:
|
|
readable, read_as => read_field
|
|
read_as => check_field
|
|
read_clears, write_to_clear(v) => clear_field
|
|
writeable => write_field
|
|
|
|
* Register fields that can be given an initial value: initial(val)
|
|
|
|
* Variant format types. A format can have several different
|
|
variants. Some fields are common to all variants, others only
|
|
appear in some variants. There must be a way of determining which
|
|
variant a format is
|
|
|
|
* Back-end to print out memory map of device, or even pictures.
|
|
* Selectively enable/disable specific checks
|
|
* Collect all parse errors before exiting.
|
|
* Generate device models.
|
|
|
|
* Devil's notion of 'values', derived from registers (e.g. by
|
|
concatenating two fields from different registers together). This
|
|
should allow constant parts of values: for example, in PTEs, one
|
|
needs to concat a bit field with a bunch of zeros to get an
|
|
address.
|
|
|
|
* Preconditions and postconditions, e.g. for handling register select
|
|
registers. These also tend to imply software state variables used
|
|
to track the state of the hardware for the driver.
|
|
|
|
* Higher-level state machine constraints on how to program the
|
|
hardware. For example, to read the ESR below you must first
|
|
write once to it. To clear it you need to perform two
|
|
back-to-back writes. Don't ask....
|
|
|
|
* Messages, as opposed to addresses. Composing access to devices
|
|
that can only be accessed by sending messages using writes to
|
|
|
|
* Register overlap checks should allow non-'also' registers to
|
|
overlap if one is non-writeable and the other is non-readable.
|
|
|
|
* Compile time checks:
|
|
- Duplicate register names
|
|
- Duplicate register type names
|
|
- Overlapping address spaces
|
|
- Type mismatch between device arg and register base
|
|
- Undefined register types
|
|
|
|
* What are the name spaces?
|
|
- Register and constant types
|
|
- Register names
|
|
- Constant value names
|
|
- Fields within a particular format
|
|
- Device parameters
|
|
|
|
* Good (bad) test cases for Mackerel:
|
|
- PageTableEntries for x86, with alternative values
|
|
(e.g. superpages)
|
|
- ARM coprocessor registers
|
|
- x86 CPUID instructions, including clockout of cache params
|
|
- Access modes for LPC PIC, DMAC, and Clock hardware
|
|
|
|
|
|
-------------------------
|
|
Experiences:
|
|
|
|
- In contrast to Devil, accessing device registers is easier these
|
|
days since there is lots of address space. Consequently,
|
|
pre-conditions on registers and indexes are much less of an issue,
|
|
and Mackerel doesn't support them
|
|
|
|
- Mackerel specs are intended to be as close as possible to the data
|
|
sheets. While Devil wanted manufacturers to publish Devil specs
|
|
along with data sheets, we are less idealistic. We want to make it
|
|
as easy as possible for a programmer to type in a spec from teh
|
|
data sheet.
|
|
|
|
- What is hard these days is that registers have lots of fields, and
|
|
reserved fields which must be read before being written.
|
|
|
|
- HyperV MSR problem: RSVD registers.
|
|
|
|
- It was invaluable having working driver for APIC while debugging
|
|
the Mackerel version. Hopefully this is not the case once Mackerel
|
|
is debugging and reliable.
|
|
|
|
- It has got to be reliable: you need to feel comfortable Mackeral
|
|
isn't crashing your code.
|
|
|
|
- Mackerel code is transparent: it's readable, and intuitive.
|
|
|
|
- Most existing code uses (a) mapped structs, (b) bitfields, (c)
|
|
Macros giving masks and shifts
|
|
|
|
- We can do full device dumps
|
|
|
|
- Source code inline is shorter and more intuitive.
|
|
|
|
- Specs are easier to write than structs or macros.
|
|
|
|
- Mackerel errors do get caught (esp. register widths catch incorrect
|
|
field sizes).
|
|
|
|
- We build all our drivers using Mackerel now, plus MSRs.
|
|
|
|
- Haskell was good. Parsing, etc. Formatting.
|
|
|
|
- Generated code is remarkably efficient. Assembly looks almost the
|
|
same.
|
|
|
|
- Same file used for user and kernel space.
|
|
|
|
- Lots of volatile errors. Fix once in Mackerel.h, and then done.
|
|
Programmer is saved from having to deal with them further.
|
|
|
|
- Still dependent on GCC features, but fewer of them appear in inline
|
|
code and those that do (e.g. inline struct literals) are more
|
|
syntactic sugar than code directives like volatile. This makes us
|
|
more compiler independent.
|
|
|
|
- Descriptions are really nice. Lots of ad-hoc code got written to
|
|
do this. snprintf is the right way.
|
|
|
|
- Mackerel-generated files are large, with many types, and many
|
|
inlined functions. Generated code, however, is very compact.
|
|
|
|
- List devices so far.
|
|
|
|
- RWCS, RWS, etc. can be supported (e.g. device models).
|
|
|
|
- Code inflates by approx. 10x (.dev -> .h)
|
|
|
|
- Takes a while to type in all the registers, but (1) it's easier
|
|
than working out struct formats, (2) it's basically transcribing
|
|
the data sheet, so a good way of getting acquainted with the
|
|
device.
|
|
|
|
-----------------------
|
|
|
|
acpi_ec.dev:1:1: File acpi_ec.dev describes dev EC not acpi_ec
|
|
arm_icp_pic.dev:1:1: File arm_icp_pic.dev describes dev arm_icp_pic0 not arm_icp_pic
|
|
hpet.dev:1:1: File hpet.dev describes dev HPET not hpet
|
|
ixp2800_icp_pic.dev:1:1: File ixp2800_icp_pic.dev describes dev ixp2800_icp_pic0 not ixp2800_icp_pic
|
|
ixp2800_uart.dev:1:1: File ixp2800_uart.dev describes dev IXP2800_UART not ixp2800_uart
|
|
lpc_dma.dev:1:1: File lpc_dma.dev describes dev LPC_dma not lpc_dma
|
|
lpc_ioapic.dev:1:1: File lpc_ioapic.dev describes dev LPC_IOAPIC not lpc_ioapic
|
|
lpc_pic.dev:1:1: File lpc_pic.dev describes dev LPC_PIC not lpc_pic
|
|
lpc_rtc.dev:1:1: File lpc_rtc.dev describes dev LPC_rtc not lpc_rtc
|
|
lpc_rtc_spaces.dev:1:1: File lpc_rtc_spaces.dev describes dev LPC_rtc not lpc_rtc_spaces
|
|
lpc_timer.dev:1:1: File lpc_timer.dev describes dev LPC_timer not lpc_timer
|
|
pc16550d_uart.dev:1:1: File pc16550d_uart.dev describes dev PC16550D_UART not pc16550d_uart
|
|
pl011_uart.dev:1:1: File pl011_uart.dev describes dev PL011_UART not pl011_uart
|