mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-24 06:07:52 -04:00
228 lines
No EOL
12 KiB
Text
228 lines
No EOL
12 KiB
Text
<<<
|
|
:sectnums:
|
|
==== One-Wire Serial Interface Controller (ONEWIRE)
|
|
|
|
[cols="<3,<3,<4"]
|
|
[grid="none"]
|
|
|=======================
|
|
| Hardware source files: | neorv32_onewire.vhd |
|
|
| Software driver files: | neorv32_onewire.c | link:https://stnolting.github.io/neorv32/sw/neorv32__onewire_8c.html[Online software reference (Doxygen)]
|
|
| | neorv32_onewire.h | link:https://stnolting.github.io/neorv32/sw/neorv32__onewire_8h.html[Online software reference (Doxygen)]
|
|
| Top entity ports: | `onewire_i` | 1-bit 1-wire bus sense input
|
|
| | `onewire_o` | 1-bit 1-wire bus output (pull low only)
|
|
| Configuration generics: | `IO_ONEWIRE_EN` | implement ONEWIRE interface controller when `true`
|
|
| | `IO_ONEWIRE_FIFO` | RTX fifo depth, has to be zero or a power of two, min 1
|
|
| CPU interrupts: | fast IRQ channel 13 | operation done interrupt (see <<_processor_interrupts>>)
|
|
|=======================
|
|
|
|
|
|
**Overview**
|
|
|
|
The NEORV32 ONEWIRE module implements a single-wire interface controller that is compatible to the
|
|
Dallas/Maxim 1-Wire protocol, which is an asynchronous half-duplex bus requiring only a single signal wire
|
|
(plus ground) for communication.
|
|
|
|
The bus is based on a single open-drain signal. The controller as well as all devices on the bus can only pull-down
|
|
the bus (similar to TWI/I2C). The default high-level is provided by a single pull-up resistor connected to the positive
|
|
power supply close to the bus controller. Recommended values are between 1kΩ and 10kΩ depending on the bus
|
|
characteristics (wire length, number of devices, etc.).
|
|
|
|
|
|
**Tri-State Drivers**
|
|
|
|
The ONEWIRE module requires a tristate driver (actually, just an open-drain driver) for the 1-wire bus line, which has
|
|
to be implemented in the top module / IO ring of the design. A generic VHDL example is given below (`onewire_io` is the
|
|
actual 1-wire bus signal, which is of type `std_logic`; `onewire_o` and `onewire_i` are the processor's ONEWIRE port signals).
|
|
|
|
.ONEWIRE VHDL Tristate Driver Example
|
|
[source,VHDL]
|
|
----
|
|
onewire_io <= '0' when (onewire_o = '0') else 'Z'; -- drive (low)
|
|
onewire_i <= std_ulogic(onewire_io); -- sense
|
|
----
|
|
|
|
|
|
**Theory of Operation**
|
|
|
|
The ONEWIRE controller provides two interface registers: `CTRL` and `DCMD.` The control register (`CTRL`)
|
|
is used to configure the module and to monitor the current state. The `DCMD` register, which can optionally
|
|
by buffered by a configurable FIFO (`IO_ONEWIRE_FIFO` generic), is used to read/write data from/to the bus
|
|
and to trigger bus operations.
|
|
|
|
The module is enabled by setting the `ONEWIRE_CTRL_EN` bit in the control register. If this bit is cleared, the
|
|
module is automatically reset, any bus operation is aborted, the bus is brought to high-level (due to the external
|
|
pull-up resistor) and the internal FIFO is cleared. The basic timing configuration is programmed via a coarse clock
|
|
prescaler (`ONEWIRE_CTRL_PRSCx` bits) and a fine clock divider (`ONEWIRE_CTRL_CLKDIVx` bits).
|
|
|
|
The controller can execute four basic bus operations, which are triggered by writing the according command bits
|
|
in the `DCMD` register (`ONEWIRE_DCMD_DATA_*` bits) while also writing the actual data bits (`ONEWIRE_DCMD_CMD_*`
|
|
bits).
|
|
|
|
[start=1]
|
|
. `0b00` (`ONEWIRE_CMD_NOP`) - no operation (dummy)
|
|
. `0b01` (`ONEWIRE_CMD_BIT`) - transfer a single-bit (read-while-write)
|
|
. `0b10` (`ONEWIRE_CMD_BYTE`) - transfer a full-byte (read-while-write)
|
|
. `0b11` (`ONEWIRE_CMD_RESET`) - generate reset pulse and check for device presence
|
|
|
|
Every command (except NOP) will result in a bus operation when dispatched from the data/command FIFO.
|
|
Each command (except NOP) will also sample a bus response (a read bit, a read byte or a presence pulse) to a
|
|
shadowed receive FIFO that is accessed when reading the `DCMD` register.
|
|
|
|
When the single-bit operation (`ONEWIRE_CMD_BIT`) is executed, the data previously written to `DCMD[0]` will
|
|
be send to the bus and the response is sample to `DCMD[7]`. Accordingly, a full-byte transmission (`ONEWIRE_CMD_BYTE`)
|
|
will send the byte written to `DCMD[7:0]` to the bus and will sample the response to `DCMD[7:0]` (LSB-first). Finally, the
|
|
reset command (`ONEWIRE_CMD_RESET`) will generate a bus reset and will also sample the "presence pulse" from the device(s)
|
|
to the `DCMD[ONEWIRE_DCMD_PRESENCE]`.
|
|
|
|
.Read from Bus
|
|
[NOTE]
|
|
In order to read a single bit from the bus `DCMD[0]` has to set to `1` before triggering the bit transmission
|
|
operation to allow the accessed device to pull-down the bus. Accordingly, `DCMD[7:0]` has to be set to `0xFF` before
|
|
triggering the byte transmission operation when the controller shall read a byte from the bus.
|
|
|
|
As soon as the current bus operation has completed (and there are no further operations pending in the FIFO) the
|
|
`ONEWIRE_CTRL_BUSY` bit in the control registers clears.
|
|
|
|
|
|
**Bus Timing**
|
|
|
|
The control register provides a 2-bit clock prescaler select (`ONEWIRE_CTRL_PRSC`) and a 8-bit clock divider
|
|
(`ONEWIRE_CTRL_CLKDIV`) for timing configuration. Both are used to define the elementary base time T~base~.
|
|
All bus operations are timed using multiples of this elementary base time.
|
|
|
|
.ONEWIRE Clock Prescaler Configurations
|
|
[cols="<4,^1,^1,^1,^1"]
|
|
[options="header",grid="rows"]
|
|
|=======================
|
|
| **`ONEWIRE_CTRL_PRSC[2:0]`** | `0b00` | `0b01` | `0b10` | `0b11`
|
|
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64
|
|
|=======================
|
|
|
|
Together with the clock divider value (`ONEWIRE_CTRL_PRSCx` bits = `clock_divider`) the base time is defined by the
|
|
following formula:
|
|
|
|
_**T~base~**_ = (1 / _f~main~[Hz]_) * `clock_prescaler` * (`clock_divider` + 1)
|
|
|
|
Example:
|
|
|
|
* _f~main~_ = 100MHz
|
|
* clock prescaler select = `0b01` -> `clock_prescaler` = 4
|
|
* clock divider `clock_divider` = 249
|
|
|
|
_**T~base~**_ = (1 / 100000000Hz) * 4 * (249 + 1) = 10000ns = **10µs**
|
|
|
|
The base time is used to coordinate all bus interactions. Hence, all delays, time slots and points in time are
|
|
quantized as multiples of the base time T~base~. The following images show the two basic operations of the ONEWIRE
|
|
controller: single-bit (0 or 1) transaction and reset with presence detect. Note that the full-byte operations just repeats
|
|
the single-bit operation eight times. The relevant points in time are shown as _absolute_ time points (in multiples of the
|
|
time base T~base~) with the falling edge of the bus as reference points.
|
|
|
|
.Single-bit data transmission (not to scale)
|
|
[wavedrom, format="svg", align="center"]
|
|
----
|
|
{ signal: [
|
|
{ wave: '10x....1.', node: '.........'},
|
|
{ node: '.ab......'},
|
|
{ node: '.X..c....'},
|
|
{ node: '.Y.....d.'},
|
|
{ node: '.Z......e'}
|
|
],
|
|
edge: [
|
|
'a-b t0',
|
|
'X-c t1',
|
|
'Y-d t2',
|
|
'Z-e t3'
|
|
]
|
|
}
|
|
----
|
|
|
|
.Reset pulse and presence detect (not to scale)
|
|
[wavedrom, format="svg", align="center"]
|
|
----
|
|
{ signal: [
|
|
{ wave: '10..x...1', node: '.........'},
|
|
{ node: '.f..g....'},
|
|
{ node: '.X.. h...'},
|
|
{ node: '.Y......i'}
|
|
],
|
|
edge: [
|
|
'f-g t4',
|
|
'X-h t5',
|
|
'Y-i t6',
|
|
]
|
|
}
|
|
----
|
|
|
|
.Data Transmission Timing
|
|
[cols="<2,<6,^3,^3"]
|
|
[options="header",grid="rows"]
|
|
|=======================
|
|
| Symbol | Description | Multiples of T~base~ | Time when T~base~ = 10µs
|
|
4+^| **Single-bit data transmission**
|
|
| `t0` (a->b) | Time until end of active low-phase when writing a `'1'` or when reading | 1 | 10µs
|
|
| `t1` (a->c) | Time until controller samples bus state (read operation) | 2 | 20µs
|
|
| `t2` (a->d) | Time until end of bit time slot (when writing a `'0'` or when reading) | 7 | 70µs
|
|
| `t3` (a->e) | Time until end of inter-slot pause (= total duration of one bit) | 9 | 90µs
|
|
4+^| **Reset pulse and presence detect**
|
|
| `t4` (f->g) | Time until end of active reset pulse | 48 | 480µs
|
|
| `t5` (f->h) | Time until controller samples bus presence | 55 | 550µs
|
|
| `t6` (f->i) | Time until end of presence phase | 96 | 960µs
|
|
|=======================
|
|
|
|
.Default Timing Parameters
|
|
[NOTE]
|
|
The "known-good" default values for base time multiples were chosen for stable and reliable bus
|
|
operation and not for maximum throughput.
|
|
|
|
The absolute points in time are hardwired by the VHDL code and cannot be changed during runtime.
|
|
However, the timing parameter can be customized (if necessary) by editing the ONEWIRE's VHDL source file.
|
|
The times t0 to t6 correspond to the previous timing diagrams.
|
|
|
|
.Hardwired timing configuration in `neorv32_onewire.vhd`
|
|
[source,VHDL]
|
|
----
|
|
-- timing configuration (absolute time in multiples of the base tick time t_base) --
|
|
constant t_write_one_c : unsigned(6 downto 0) := to_unsigned( 1, 7); -- t0
|
|
constant t_read_sample_c : unsigned(6 downto 0) := to_unsigned( 2, 7); -- t1
|
|
constant t_slot_end_c : unsigned(6 downto 0) := to_unsigned( 7, 7); -- t2
|
|
constant t_pause_end_c : unsigned(6 downto 0) := to_unsigned( 9, 7); -- t3
|
|
constant t_reset_end_c : unsigned(6 downto 0) := to_unsigned(48, 7); -- t4
|
|
constant t_presence_sample_c : unsigned(6 downto 0) := to_unsigned(55, 7); -- t5
|
|
constant t_presence_end_c : unsigned(6 downto 0) := to_unsigned(96, 7); -- t6
|
|
----
|
|
|
|
.Overdrive Mode
|
|
[NOTE]
|
|
The ONEWIRE controller does not support the overdrive mode natively. However, it can be implemented by reducing
|
|
the base time **T~base~** (and by eventually changing the hardwired timing configuration in the VHDL source file).
|
|
|
|
|
|
**Interrupt**
|
|
|
|
A single interrupt is provided by the ONEWIRE module to signal "idle" condition to the CPU. Whenever the
|
|
controller is idle (again) and the data/command FIFO is empty, the interrupt becomes active.
|
|
|
|
|
|
**Register Map**
|
|
|
|
.ONEWIRE register map (`struct NEORV32_ONEWIRE`)
|
|
[cols="<4,<2,<6,^2,<6"]
|
|
[options="header",grid="all"]
|
|
|=======================
|
|
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
|
|
.11+<| `0xfff20000` .11+<| `CTRL` <|`0` `ONEWIRE_CTRL_EN` ^| r/w <| ONEWIRE enable, reset if cleared
|
|
<|`1` `ONEWIRE_CTRL_CLEAR` ^| -/w <| clear RXT FIFO, auto-clears
|
|
<|`3:2` `ONEWIRE_CTRL_PRSC1 : ONEWIRE_CTRL_PRSC0` ^| r/w <| 2-bit clock prescaler select
|
|
<|`11:4` `ONEWIRE_CTRL_CLKDIV7 : ONEWIRE_CTRL_CLKDIV0` ^| r/w <| 8-bit clock divider value
|
|
<|`14:12` - ^| r/- <| _reserved_, read as zero
|
|
<|`18:15` `ONEWIRE_CTRL_FIFO_MSB : ONEWIRE_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(`IO_ONEWIRE_FIFO`)
|
|
<|`27:19` - ^| r/- <| _reserved_, read as zero
|
|
<|`28` `ONEWIRE_CTRL_TX_FULL` ^| r/- <| TX FIFO full
|
|
<|`29` `ONEWIRE_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available
|
|
<|`30` `ONEWIRE_CTRL_SENSE` ^| r/- <| current state of the bus line
|
|
<|`31` `ONEWIRE_CTRL_BUSY` ^| r/- <| operation in progress when set or TX FIFO not empty
|
|
.4+<| `0xfff20004` .4+<| `DCMD` <|`7:0` `ONEWIRE_DCMD_DATA_MSB : ONEWIRE_DCMD_DATA_LSB` ^| r/w <| receive/transmit data
|
|
<|`9:8` `ONEWIRE_DCMD_CMD_HI : ONEWIRE_DCMD_CMD_LO` ^| -/w <| operation command LSBs
|
|
<|`10` `ONEWIRE_DCMD_PRESENCE` ^| -/w <| bus presence detected
|
|
<|`31:11` - ^| r/- <| _reserved_, read as zero
|
|
|======================= |