#
# This file is part of LUNA.
#
# Copyright (c) 2020-2025 Great Scott Gadgets <info@greatscottgadgets.com>
# SPDX-License-Identifier: BSD-3-Clause
""" A simple timer peripheral. """
from amaranth import *
from amaranth.lib import enum, wiring
from amaranth.lib.wiring import In, Out, flipped, connect
from amaranth_soc import csr, event
[docs]
class Peripheral(wiring.Component):
[docs]
class Enable(csr.Register, access="rw"):
"""Counter enable"""
enable: csr.Field(csr.action.RW, unsigned(1))
[docs]
class Mode(csr.Register, access="rw"):
"""Timer mode. When ``periodic`` is set to 1 the counter will automatically be reset to the reload value."""
periodic: csr.Field(csr.action.RW, unsigned(1))
[docs]
class Reload(csr.Register, access="rw"):
"""Reload value of counter."""
def __init__(self, width):
super().__init__({
"value": csr.Field(csr.action.RW, unsigned(width))
})
[docs]
class Counter(csr.Register, access="r"):
[docs]
def __init__(self, width):
"""Counter value"""
super().__init__({
"value": csr.Field(csr.action.R, unsigned(width))
})
def __init__(self, *, width):
if not isinstance(width, int) or width < 0:
raise ValueError("Counter width must be a non-negative integer, not {!r}"
.format(width))
if width > 32:
raise ValueError("Counter width cannot be greater than 32 (was: {})"
.format(width))
self.width = width
# registers
regs = csr.Builder(addr_width=4, data_width=8)
self._enable = regs.add("enable", self.Enable())
self._mode = regs.add("mode", self.Mode())
self._reload = regs.add("reload", self.Reload(width))
self._counter = regs.add("counter", self.Counter(width))
self._bridge = csr.Bridge(regs.as_memory_map())
# events
from typing import Annotated
EventSource = Annotated[event.Source, "Interrupt that occurs when the timer reaches zero."]
self._zero = EventSource(trigger="rise", path=("zero",))
event_map = event.EventMap()
event_map.add(self._zero)
self._events = csr.event.EventMonitor(event_map, data_width=8)
# csr decoder
self._decoder = csr.Decoder(addr_width=5, data_width=8)
self._decoder.add(self._bridge.bus)
self._decoder.add(self._events.bus, name="ev")
super().__init__({
"bus": Out(self._decoder.bus.signature),
"irq": Out(unsigned(1)),
})
self.bus.memory_map = self._decoder.bus.memory_map
[docs]
def elaborate(self, platform):
m = Module()
m.submodules += [self._bridge, self._events, self._decoder]
# connect bus
connect(m, flipped(self.bus), self._decoder.bus)
# peripheral logic
zero = Signal()
finish = Signal()
with m.If(self._enable.f.enable.data):
with m.If(self._counter.f.value.r_data == 0):
m.d.comb += zero.eq(~finish)
with m.If(self._mode.f.periodic.data):
m.d.sync += self._counter.f.value.r_data.eq(self._reload.f.value.data)
with m.Else():
m.d.sync += finish.eq(1)
with m.Else():
m.d.sync += self._counter.f.value.r_data.eq(self._counter.f.value.r_data - 1)
with m.Else():
m.d.sync += self._counter.f.value.r_data.eq(self._reload.f.value.data)
m.d.sync += finish.eq(0)
# connect events to irq line
m.d.comb += [
self._zero.i .eq(zero),
self.irq .eq(self._events.src.i),
]
return m