Skip to content

Instantly share code, notes, and snippets.

@tinyfpga
Created January 21, 2018 23:04
Show Gist options
  • Save tinyfpga/a646c8681166188fc7769d80d425e876 to your computer and use it in GitHub Desktop.
Save tinyfpga/a646c8681166188fc7769d80d425e876 to your computer and use it in GitHub Desktop.
/**
* Simple UART module to explore basic HardwareC concepts.
*
* HardwareC is a working name for a new hardware description language. The
* goal is to make FPGAs easier for hobbyists to take advantage of. To achieve
* this goal, some design choices have been made:
*
* 1. Use familiar syntax. C/C++ syntax is borrowed everywhere, no reason to
* reinvent the wheel. Where C/C++ falls short, borrow from Verilog/SystemVerilog.
* 2. Interrop with C/C++. A HardwareC module should be able to be used seamlessly
* in an Arduino sketch targetting an FPGA board. Function calls should be compatible
* with C/C++.
* 3. HardwareC is _not_ high level synthesis. It will do what you tell it to
* do and it will not make complicated micro-architecture decisions for you.
* 4. Functions and threads are procedures that run over time like in C/C++. They are
* converted to statemachines or microcoded machines along with the corresponding
* data-path.
*
* Please poke holes in this simple example and ask questions.
*/
module uart <
type tx_buffer_t implements fifo,
type rx_buffer_t implements fifo
> (
output logic tx,
input logic rx
) {
tx_buffer_t tx_buffer;
rx_buffer_t rx_buffer;
// module
function void write(uint8_t data) {
tx_buffer.put(data);
}
function uint8_t read() {
return rx_buffer.get();
}
function bool has_data() {
return !rx_buffer.is_empty();
}
thread tx_data {
while (1) {
uint8_t tx_data = tx_buffer.get();
// start bit
tx = 1;
wait 100us;
// send data
for (uint8_t i = 0; i < 8; i++) {
tx = data[i];
wait 100us;
}
// stop bit
tx = 0;
wait 100us;
}
}
thread rx_data {
while (1) {
// wait for start bit
wait until rx = 1;
wait 150us;
// shift in data
uint8_t data = 0;
for (uint8_t i = 0; i < 8; i++) {
data[i] = rx;
wait 100us;
}
// stop bit
wait 100us;
rx_buffer.put(data);
}
}
}
@toastedcornflakes
Copy link

Looks cool!

I'm not sure I'm 100% behind the implicit clock (and reset) though. Why not make clock an explicit input of the module and the wait() functions?

@kenkeiter
Copy link

I like where you're going with this!

It could be nice to choose a newer language to model the syntax after, though (i.e. Rust). This would make the language more difficult to implement, but also give you a slightly more future-forward edge to increase likelihood of adoption. The trait-esque stuff you've got going on at the top ([...] implements [...]) might translate well in a more Rust-like syntax, too.

I agree with @toastedcornflakes on the explicitness of the clock, to some degree, but I can understand why you'd want to retain a behavior that's more analogous to the way the non-FPGA world thinks about clocks.

Overall: very cool. Excited to see more 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment