 So hi everyone. I'm Mary and I'm here to talk to you today about using SAIL to generate a GNU assembler, disassembler simulator for the RISC-5 toolchain. A little bit about me to start. I'm just an undergraduate student studying at the University of Surrey for Electronic and Electrical Engineering. I'm a UK ESF Scholar with Ember Cosm and I am the group chair for the RISC-5 Foundation's Open Source and University Outreach Task Group. So if you all qualify to be in that task group, so please come join us and if you're interested come talk to me after this presentation. So in the past, creating a new processor has consisted of defining a formal specification and then creating an assembler and compiler toolchain definition from this by hand. The operation of the assembler and compiler can only be verified once the processor has been manufactured, which introduces a linear dependency. The time scale could be significantly reduced if the definitions could be generated automatically from the specification, but this has proved difficult in the past. However, advances have been made with the processor specification language and framework to generate the assembler and compilers, so it should now be possible. That's where my final year undergraduate project comes in. It aims to develop code in a functional programming language that creates the main file required by the toolchain generator from the processor description with minimum human interaction. The RISC-5 architecture, more specifically RV64i, will be used as the model. This would represent a significant step towards a complete framework for the automatic translation. During this presentation, I will discuss the processor specification language, SAIL, show a handwritten RV64i example of SAIL, do a little bit of OCaml, and then finish off with CGEN, the CPU tool generator, and another example of RV64i, but this time for CGEN. So, to start off with, I'll give a little bit of context as why I believe this project to be important. A processor is a key component in any computer, which uses logic circuits to perform user-specified operations on data or operands. New processors are being designed all the time to fulfill different area needs, such as low-power processors for wearable tech, or high-performance graphics processors. To allow these modifications, often processors have different blends of hardware and software, which require using special optimizations and new instructions. There are some key steps in the development life cycle of a processor. These are designing the physical hardware for the processor, defining the processor's optimizations, writing the ISA, creating the simulator of the ISA, running pre-silicon tests, which is basically where you take the ISA simulator and the hardware model and run them together, and then finally manufacture the design. But designing a processor from scratch takes too long and is too expensive. Our estimates that it takes about 300 engineer years to complete a new, reasonable design. So, often there is a need to use third-party designs such as ARM or an open-source design such as Visk 5. Once an ISA is defined, the next step is to write the assembler and disassembler and the other CPU tools. This step is the one that stops many organizations from going down the custom processor route as it takes a long time and there is no guarantee of success. But it can be done with an open-source CPU-generating tool called CGen. But this leaves the problem of converting the hardware design of the processor to CGen. Until recently there was a lack of formal methods to define the hardware in such a manner. The automated analysis could be performed to confirm that the design actually does what is required. SALE was developed to fulfill this niche. The formal model is written in SALE, a language describing the instruction set architecture semantics of a processor. It is a work-in-progress by the University of Cambridge. SALE aims to provide an engineer-friendly, vendor-seudo-code-like language for describing instruction semantics. It is essentially a first-order imperative language, so it uses predicates which can define non-logical objects. An example, sentence would be there exists x such that x is Socrates and x is a man. But SALE also has lightweight dependent typing for numeric types and bit vector lengths, which are very data structures that compactly stores bits. These are automatically checked using Z3, the Prover, not the BMW. It has been used for several papers available from the University of Cambridge SALE website, the link is here. Given a SALE definition, the tool will check it and generate executable emulators in C and O camel, theorem-prover definitions for Isabel and Hawk 4, and definitions to integrate with the REM tool for currency semantics. To introduce the features in more detail, I will now go through a simple RV64I example. The RV64I in SALE is handwritten. We will start with some basic type synonyms. We create a type x then underscore t for bit vectors of length 64. Then we define a type regino, which is a type synonym for the built-in type atom. The type atom is a number which is exactly equal to the type variable atom. Type variables are semantically marked with single quotes as an ML. A constraint can be attached to this type of synonym, ensuring that it is only used when we can guarantee that its value will be between 0 and 31. SALE supports a rich variety of numeric types, including range types which are statistically checked. We can then define a synonym, regbits, for bits 5. We don't want to manually convert between regbits and regino all the time, so we define a function that maps between them and declare it as a cast, which allows the type checker to insert it where needed. By default, SALE does not do any automatic casting, except between basic numeric types when safe. But to allow idioms in ISA vendor description documents, SALE supports flexible user-defined casts. To ensure that the constraint on the regino type synonym is satisfied, we return a quantified type. Now we set up some basic architectural state. First, creating a register of type xlen underscore t for both the program counter PC and the next program counter, next PC. We define the general purpose registers as a vector of 32, xlen underscore t, bit vectors. The deck keyword isn't important in this example, but SALE supports two different numbering systems. We then define a guesser and setter for the registers, which ensures that the zero register is treated specially. In risk five, register zero is always hardcoded to be zero. Finally, we overload both the readRx and writeWx functions as simply x. This allows us to write registers as xr equals value and read registers as value equals xr. SALE supports flexible ad hoc overloading and has an impressive language in assignments with the aim of allowing pseudocode-like definitions. We also give a function memR for reading memory. This function just points to a bulletin we have to find elsewhere. Note that functions in SALE are annotated with effects. The effect system is quite basic, but indicates whether or not functions read or write registers, rreg and wreg. Read and write memory, rmem and wmem, as well as a host of other concurrency model related effects. They also indicate whether a function throws exceptions or has other non-local control flow. The escape effect, for example. It is common when defining architecture specifications to break instruction semantics down into separate functions that handle decoding, possibly even in several stages, into custom intermediate data types and executing the decoded instructions. However, it is often desirable to group the relevant parts of these functions and data types together in one place, as they would usually be found in an architecture reference manual. To support this, SALE supports scattered definitions. We first give types for the execute and decode functions and declare them as scattered functions, as well as the AST union. Now we provide the causes for the ADAMEDIA AST type, as well as its execute and download causes. We can define the decode and the decode causes. We can define the decode function by directly pattern matching on the bit vector representing the instruction. SALE supports vector concatenation patterns. For example, the symbol AT is the vector concatenation operator and uses the types provided, bits 12 and reg bits, to destructure the vector in the correct way. We use the EXTS library function that sign extends its argument. Now we do the same thing for the low double instruction. Finally, we define the fall through case for the decode function and end all our scattered definitions. Note that the causes in a scattered function will be matched in the order they appear in the file. The SALE backend is written in OCaml, which is an impure functional programming language like Python. Functional languages are based on mathematical functions opposed to procedural languages like C, which depends on flow statements such as if or for loops. The backend generates executable emulators in C and OCaml and through improver definitions from the SALE model. One of the highlights of OCaml is its parametric polymorphism and type inference, which allows operations to be written independently of the elements. This is useful for architectures like RISC-5 that have multiple sizes of instruction, since a single operation can be used on all sizes. But how about a small example to show you a better vocabulary of OCaml? We've got the cable. The laptop's on as a battery. Technical problems. Looks like it must have gone to sleep. I think it turned off. Maybe you can take some questions or something. Yeah. I mean... Pardon? Yeah, it's rebooting at the moment. I can take questions for what's gone through so far, I guess. So, the question was, my effects model or the SALE effect model says reading an address of memory. Does it need to say a specific address? I'm not the best expert on SALE. I can put you in touch with Peter Sowell, who's the guy running Cambridge. I can give you his email afterwards, but I don't know at the moment. Any other questions? It means that the specification needs to be written in formal language. So, the question was, how does the specification being written in a formal language fit in? So, SALE is the formal language. It already generates stuff, but... So, that's the formal language SALE is. Yeah, in SALE. So, there'll be a risk five definition written in the SALE language, which will then be taken through OCaml into CGEN, which I'll come into in a moment. It didn't appease the presentation gods enough today, I guess. The assembly of the source code. After, it's the copied source code. What you used to form a copied source code is the most performance. It's the GCC. Are you just a GCC or use more software for the copied source code? So, the question was, do I use more than just GCC for the source code? As opposed to LLVM or another compiler? Or... I didn't think I fully understand what you mean. Yeah, it's GCC. So, the assembler uses gas, and it fits into new tails. Well, shall I carry on? I get technical support to help. So, I'm going to show you an example of some OCaml. It's quite simple. It'll be a sourcing algorithm. I'm sure we've all seen them before. But it'll demonstrate the polymorphic ability. So, first, it's going to define the function, the sort algorithm, and it's going to have a list input and a list output. These will be shown by two square brackets with an arrow in between, the input coming before the arrow, the output coming after the arrow. The function will prepend x onto one and calls the insert function. The input of the function can either be a list of integers or a list of strings. This is the polymorphic ability. I'll carry on to Siegen. At university, my final year project is to use the sale description of risk 5 to generate low-level tools, namely assemblers and disassemblers. For this, I am using Siegen, which is part of the GNU tool chain. Siegen is an open-source CPU tool generator which uses a framework to generate assemblers, disassemblers and simulators. The generator is written in scheme, a functional language like OCaml, which makes the conversion easier. Siegen ports to GNU by adding a scheme description to LibOp codes, which is the GNU's library of opcode, up around codes. LibOp codes can then be used to assemble or disassemble C code using GDB and GAS, respectively. There are two steps to create an assembler and disassembler using Siegen. This is first to connect Siegen to Benutils. This will allow Benutils to look for the framework files required and generate the correct opcode files. The second step, of course, is to write the framework. To connect Siegen to Benutils, the port name is needed to be added to a list of make files and configure files. The names of the required opcode files are added, too. There are four main files in Siegen, which contain the framework and additional information to create an assembler. Four risk five, these four files, are risk5.cpu, the main framework file. It's written in scheme and is the focus of this project. risk5.opc. This file contains handlers for the scheme framework. rvcpu.c. This file includes hooks, written in C, which adds more information about the hardware and ISA. And finally, rvcpu.h, the header file. The main file that I will be generating during this project will be risk5.cpu. The rest will be handwritten. Siegen must be written in a specific order to work which I will go through when the PowerPoint decides to work. I'll start off. It starts, every file starts, every risk5.cpu file or equivalent will start with include simplify.inc. This includes the instruction macros for GNU built binutils, which are used throughout the rest of the file. It's then followed by Define Arch. This function names the architecture and defines the endiness of the architecture. Define ISA is the next one, which defines the bit size of the ISA with the ISA. Define CPU describes the CPU family and instruction endiness again. Define Mac, this is the function that defines the machine. Define unit, this is the function that describes the model. Every machine must have a model and every model a unit. The unit will have six sections. Issue, which describes the number of operations in progress at once. One defines the latency. State lists the variable names and mode pairs. Inputs, the units of inputs. Outputs, units of outputs. And finally, profile action, which is the RTL code for function unit modelling. Once this is done, it's followed up by a definition of PMACOs. These are usually used for attributes and the instructions later on. They are also used to simplify the file. This is then followed by define hardware, which defines the hardware simulated in the program. This can include the program counter and other registers. A PMACO can be used to define the register names if there are many. DNF and DF are the functions that describe the instruction fields for registers and immediate. Define multi-field defines instruction fields which are made up of unconnected bit fields. Define operands. Defines the operands used and the instructions. It requires an instruction field. Then, then finally, defining the instructions. This function defines the instruction format. The instructions add and sub use the same bit format. Therefore, it is simply, it's simple to apply a PMACO to the format. In the instructions, hash B is used to define binary numbers. Multiple functions use attributes, which defines the ISA and machine of the instruction. This is shown by base ISIS. Once this file is written and the basic hooks are added to the other three files, make stamp followed by running a make command in benutils can be used to generate the assembler and disassembler. To conclude, being able to generate an assembler from the formal specification of an ISA would be very useful. The timescale for processor development could be significantly reduced if the definitions could be generated automatically from the specification. But this has proved difficult in the past. However, advances have been made with the processor specification language and the framework to generate assemblers, disassemblers and simulators. So it's now possible. This is just a status report for my project which is far from done, but there is a clear aim and a method. Thank you for listening. I apologise about the technical problems. Can you speak up a bit, please? We don't have any figures where I don't think so quite yet. Do we? We'll keep a note of that. Any other questions? Oh, sorry. You can sit using an FTTA for simulations. We've looked into veralog a bit, but I don't mind expanding into hardware programming for future projects. There's no more questions. I think that's the end. Thank you.