Created
January 21, 2018 23:04
-
-
Save tinyfpga/a646c8681166188fc7769d80d425e876 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} | |
} | |
} |
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
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?