Closed Rewbert closed 3 years ago
When requesting a LED the user is now given a reference to the LED and a handler that is to be scheduled. There is now a function schedule
which is used to schedule the things that should populate the ready queue when the program begins. E.g
testprogram :: Compile ()
testprogram = do
x <- switch 0
y <- switch 1
(z, handler) <- onoffLED 0
let ?sw0 = x
?sw1 = y
?led_ctl = z
schedule mmain
schedule handler
By changing the ordering of schedule mmain
and schedule handler
you can change the eagerness with which the handler actually performs the LED IO.
So there is a lot going on in this PR. I will try to summarize the changes:
Split larger modules up into smaller modules
The directories SSM/Core/
and SSM/Frontend/
had some modules that were quite big and contained many different definitions. When trying to add support for peripherals I ran into some cyclic dependencies. The solution to this was to be more fine-grained about what I wanted to import.
Support for peripherals I tried to think about how we could best add support for peripherals while keeping it extensible. I did not want to add it in a way that hinted at anything about us generating C. I definitely see us being interested in adding different backends etc. The project structure looks like this:
SSM/ # Main directory
Core/ # Core representation. Pretty-printer, compiler, interpreter etc operate on ASTs defined in this directory
Frontend/ # Frontend module that exposes the API programmers use to write programs
Backend/ # Directory containing backends
C/ # Actual C generating backend
I figured that the way a developer interacts with a peripheral might look very different from how we represent a peripheral internally. Firstly, there's a Peripheral
sub-directory in the Core
directory.
SSM/ # Main directory
Core/ # Core representation. Pretty-printer, compiler, interpreter etc operate on ASTs defined in this directory
Peripheral/ # Peripheral directory
LED.hs # Internal representation of LED peripheral
GPIO.hs # Internal representation of GPIO peripheral
Frontend/ # Frontend module that exposes the API programmers use to write programs
Backend/ # Directory containing backends
C/ # Actual C generating backend
Again, in e.g LED.hs
we describe how to build an object that represents the LED peripheral and what we want from it. Then there's a corresponding frontend module that describes how we write programs with this peripheral.
SSM/ # Main directory
Core/ # Core representation. Pretty-printer, compiler, interpreter etc operate on ASTs defined in this directory
Peripheral/ # Peripheral directory
LED.hs # Internal representation of LED peripheral
GPIO.hs # Internal representation of GPIO peripheral
Frontend/ # Frontend module that exposes the API programmers use to write programs
Peripheral/
LED.hs # Api for how to write programs with the LED peripheral
GPIO.hs # Api for how to write programs with the GPIO peripheral
Backend/ # Directory containing backends
C/ # Actual C generating backend
In order to collect all peripherals under a common interface to use when generating code, there's a type-class IsPeripheral
. Right now this class just specifies which references the peripheral declare in the global scope, and how they should be initialized. This definition might be extended as we add more required functionality. As we add more peripherals we will also need to extend the data type that describes the different initialization strategies, with strategies suitable for those peripherals.
Remove the special handling of global references I could reuse the peripheral machinery I describe above to implement simple global variables. They are essentially declared to belong to an 'identity' peripheral, one which performs no side effects. You can still declare and interact with variables through it though.
Add more expression operations
I added many more expression operations, such as min
, max
, %
,/
etc etc.
Change time management
As I saw a need to operate on time values as if they were expressions, I changed the internal representations of times to be denoted in nanoseconds. A time value will carry an Exp Word64
inside of it, which we can use to perform operations.
liftT :: (Exp Word64 -> Exp Word64) -> SSMTime -> SSMTime
to lift an operation on an expression to an operation on times.lift2T :: (Exp Word64 -> Exp Word64 -> Exp Word64) -> SSMTime -> SSMTime -> SSMTime
does the same thing, but with a binary operation.time2ns :: SSMTime -> Exp Word64
as an escape hatch out of a time value. What you get back is the dine denoted in nanoseconds.Added support for uint32_t
I added a module that declares a typeclass
class Waitable a where
wait :: a -> SSM ()
I then wrote some template Haskell code that generates instances of this class everything between a single reference and a 63-ary tuple (Haskells limit of tuple sizes is 63).
Oh, and the peripherals that we currently support are the switch GPIOs @j-hui used for his frequency counter, and simple LEDs. I will, after I merge this, start adding the BLE support that I discussed in our last meeting.
IO handlers. I spoke with John and will let myself be guided by this example: this example.
I've begun by adding some new expression operators: