zig-riscv-embedded

Experimental Zig-based CoAP node for the HiFive1 RISC-V board

git clone https://git.8pit.net/zig-riscv-embedded.git

  1// Copyright © 2020-2021 Sören Tempel
  2//
  3// This program is free software: you can redistribute it and/or modify
  4// it under the terms of the GNU Affero General Public License as
  5// published by the Free Software Foundation, either version 3 of the
  6// License, or (at your option) any later version.
  7//
  8// This program is distributed in the hope that it will be useful, but
  9// WITHOUT ANY WARRANTY; without even the implied warranty of
 10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 11// Affero General Public License for more details.
 12//
 13// You should have received a copy of the GNU Affero General Public
 14// License along with this program. If not, see <https://www.gnu.org/licenses/>.
 15
 16const gpio = @import("gpio.zig");
 17const plic = @import("plic.zig");
 18
 19// TODO: Extract this value using the PRCI.
 20const CLK_FREQ = 16 * 1000 * 1000; // 16 MHZ
 21
 22pub const ConfFlags = struct {
 23    tx: bool,
 24    rx: bool,
 25    cnt: u3 = 0,
 26    baud: u32 = 115200,
 27};
 28
 29fn ctrlCount(watermark: u3) u32 {
 30    return (@as(u32, watermark) & 0x07) << 16;
 31}
 32
 33pub const Uart = struct {
 34    base_addr: usize,
 35    rx_pin: gpio.Pin,
 36    tx_pin: gpio.Pin,
 37    irq: plic.Irq,
 38
 39    const Reg = enum(usize) {
 40        txfifo = 0x00,
 41        rxfifo = 0x04,
 42        txctrl = 0x08,
 43        rxctrl = 0x0c,
 44        ie = 0x10,
 45        ip = 0x14,
 46        div = 0x18,
 47    };
 48
 49    pub const ie = struct {
 50        txwm: bool,
 51        rxwm: bool,
 52    };
 53
 54    pub const FIFO_DEPTH: usize = 8;
 55    pub const TXCTRL_ENABLE: u32 = 1;
 56    pub const RXCTRL_ENABLE: u32 = 1;
 57    pub const EMPTY_MASK: u32 = 1 << 31; // for txdata/rxdata
 58    pub const IE_TXWM: u32 = 1 << 0;
 59    pub const IE_RXWM: u32 = 1 << 1;
 60
 61    fn writeWord(self: Uart, reg: Reg, value: u32) void {
 62        const ptr = @intToPtr(*volatile u32, self.base_addr + @enumToInt(reg));
 63        ptr.* = value;
 64    }
 65
 66    fn readWord(self: Uart, reg: Reg) u32 {
 67        const ptr = @intToPtr(*volatile u32, self.base_addr + @enumToInt(reg));
 68        return ptr.*;
 69    }
 70
 71    pub fn confTx(self: Uart, watermark: u3) void {
 72        self.writeWord(Reg.txctrl, TXCTRL_ENABLE | ctrlCount(watermark));
 73    }
 74
 75    pub fn confRx(self: Uart, watermark: u3) void {
 76        self.writeWord(Reg.rxctrl, RXCTRL_ENABLE | ctrlCount(watermark));
 77    }
 78
 79    pub fn readIp(self: Uart) ie {
 80        const r = self.readWord(Reg.ip);
 81        return .{
 82            .txwm = (r & IE_TXWM) != 0,
 83            .rxwm = (r & IE_RXWM) != 0,
 84        };
 85    }
 86
 87    pub fn writeIe(self: Uart, txwm: bool, rxwm: bool) void {
 88        var r: u32 = 0;
 89        if (txwm)
 90            r |= IE_TXWM;
 91        if (rxwm)
 92            r |= IE_RXWM;
 93
 94        self.writeWord(Reg.ie, r);
 95    }
 96
 97    pub fn writeByte(self: Uart, value: u8) void {
 98        self.writeWord(Reg.txfifo, value);
 99    }
100
101    fn drainInput(self: Uart) void {
102        // Read until self.readByte() returns null
103        while (self.readByte()) |_| {}
104    }
105
106    pub fn readByte(self: Uart) ?u8 {
107        const rxdata = self.readWord(Reg.rxfifo);
108        if ((rxdata & EMPTY_MASK) != 0)
109            return null;
110        return @truncate(u8, rxdata);
111    }
112
113    pub fn isTxFull(self: Uart) bool {
114        const txdata = self.readWord(Reg.txfifo);
115        return (txdata & EMPTY_MASK) != 0;
116    }
117
118    pub fn init(self: Uart, ugpio: gpio.Gpio, conf: ConfFlags) void {
119        // Enable the UART at the given baud rate
120        self.writeWord(Reg.div, CLK_FREQ / conf.baud);
121
122        if (conf.tx)
123            self.confTx(conf.cnt);
124        if (conf.rx)
125            self.confRx(conf.cnt);
126
127        if (conf.tx)
128            ugpio.setIOFCtrl(self.tx_pin, 0);
129        if (conf.rx)
130            ugpio.setIOFCtrl(self.rx_pin, 0);
131
132        if (conf.rx)
133            self.drainInput();
134    }
135};