 Hello and welcome back. This will be our final talk before our next break. And it is a slightly different setup to the other talks. This talk is being delivered by Luke Layton, who is a Libra Ethical Technology Specialist. It is pre-recorded, but Luke himself is present at this conference and available in the chat. So at the very end, we will be having a traditional Q&A. But as the video is playing, he will be available to answer questions in real time. Without further ado, let's begin. Hello and welcome to talk on the LibraSoft project for EuroPython 2021. So we use Python for pretty much everything in here. And I just want to take the opportunity to go over that. Now, it's quite a lot to cover. So I'm just apologizing in advance. That is going to be very much of an overview. But I just want to give you a glimpse into the different aspects of where we use Python in this project. So the first thing is that it's a Python simulator of the Power Instruction set, which we use for experimentation. It's only about 2,000 instructions a second, but we don't care it's written in Python. And that's more important for code clarity than anything else. We use NMEGEN for the HDL. HDL is Hardware Definition Language. Now, this includes a simulator, its own simulator. So it will do gate level simulation similar to Verilator and other simulation tools for HDL. And we then compile that to using a program called YOSIS. We output Verilog, which is a more standard language, but we treat it in this case as a machine code target. That Verilog is then handed over to Coriolis 2. Coriolis 2 is written in a mixed Python and mixed C++ in Python. It's a VLSI tool, so it's a complete toolchain. So you specify the program and the layout in a Python program, and it will then action that and create right down to the GDS2 files for you. And you can then send those to your founder and get a chip back. So the other thing is Chips for Makers have created a cell library, which is needed. Here we'll go over this later on. Chips for Makers have created a cell library specifying it in Python where you run the Python program to generate the cell libraries given what is called the PDK, the physical design kit for the Foundry. Now, in most cases, that's under NDA. However, fortunately, there's a couple out there. One of them is FreePDK45, which is an academic PDK for training students. And there is a recently, Skywater, released a 1.39-multiply PDK. Normally, you would do this under NDA because, I think, so staff himself has to run. He has specified, for example, the TSMC 180 nanometer PDK. He's got access to that. So under that NDA, he generates a cell library. He runs the program, which generates a cell library, which is suitable for TSMC 180 nanometer. He can't tell us what that is. But at the high level, we can run a parallel path along the way using the FreePDK45. So we can track what we're doing. That's what we did for the 180 nanometer ASIC when working with SOAR1 LIPSIX. Now, so I think it took you to explore NEMEDEN. It's quite an absolutely fantastic HDL, very well documented. There's tutorials online, things where you can see a thing now just briefly here. Here's your module. It has two signals. It has here, there's a count. So on each clock sync is a synchronous clock. So on the next clock cycle, count will be equal to count plus 1. And if there's an overflow detection, so if it hits a limit, it will set. That's combinatorial, so there's no time limit on that. It's always enabled, always generating a signal. And if in here, if that overflow is detected, then on the next clock cycle, the count is set to 0. Else, it's count plus equals 1. So here you can see it's just a counter. Now what's really nice is that we have standard Python classes and modules with the expected behavior of documentation, which of course, Sphinx understands and creates what you need. So it's absolutely fantastic as far as readability is concerned and expectations. Verilogue hardware engineers are not software engineering trained, and it really shows. So here's a repository that we've got. It is a proper Libra project. So we have mailing lists and mailing archives, and we also have an ISE channel, et cetera, et cetera. So development is done in real time. It's not done according to the, I'll do it when you're, I'll release it when it's ready, think we are a Libra project. So just to say, if you'd ever like to help, as you have a set of procedure we can go through, it's documented on the website. Here's how to get started. And this is much easier just by using what we call the DevEnd setup scripts. So you can just run those in an automated fashion. For example, here is that cloning of those repositories. Then running the, oh look, Python set up, I develop. That's to make sure it's in place so you can actually edit the code. We don't do Python set up with install because you'd be able to edit the code in place. Things, and off you go. And likewise one install the dependencies. Think another script there. So it's all documented needs to get set up. One for CocoDB, one for, of course, Verilog, et cetera, et cetera. So we picked OpenPower because it's a supercomputing instruction set with 25 years history. So the end user license agreement was okay. Absolutely fine. I had a very nice chat with Mendy from the OpenPower Foundation about it. And it's very reassuring. The OpenPower Foundation, I think it's been absolutely fantastic about this project. It's really quite exciting. So what we did is, as a software engineer, you don't do stuff by hand if it can be done automatically. So what we decided to do was to extract information from the OpenPower specification and to write machine, put it into a machine readable format and then first you write a decoder and a parser and including this a simulator. All right. So the first thing that to do was to decode the major, major codes and the minor op codes into tables. And these are like microcoded ops, all right. Now, these are originally so you can see that your major op code 34 is a load store operation and it's the operation instruction LVZ, own power. All right. Now, just thinking all of these actually came from MicroWatt, the MicroWatt source code which has been a fantastic reference implementation that we used. It's in VHDL, but both Anton Blanchard and the rest of the team are software engineer trained. So they put code comments in and did continuous integration unit tests and et cetera, et cetera. So we extracted these tables and put them into some machine readable CSV files. Okay. And then put them behind a wiki connecting. I'll do this one as well. So what we also did was we extracted the pseudo code from the specification. Now, and split it down into separate pages, for example. Oh, I know what's going on. My internet connection's not gone. That's annoying, but I have to wait for that to come back. Let's go and have a look at the source code here instead. So percent two. So here is the markdown files which this is for the store B instruction, for example. Now that D form there indicates that it is to be, indicates the layout of the fields. So here's your specification, ah, store word D form, all right. So there's the pseudo code, which is in the specification. Now the specification actually says, this is, we're not supposed to make this, it's not supposed to be executable. We went, ah, I forget that thing. We're making it executable. So, because you know, it's correct. You know, we know it's going to be correct. So that's store B. STW. There's STW for us as D form. Now the D form says that the layout is as follows. Okay, in bits naught to five, there'll be a number 36, which is the store W. And then RS is in bits, in fields, bits 6 to 10, RA is in 11 to 15, et cetera. Now that table, the one I've seen, percent one, find D form, mm, D form capital letters, there we go. Right, here's one of the D form. So which one was it? D there, I did, yes, this one, okay, all right. And this is a machine, we literally made it machine readable. The rest of the specification has, let's see the failed, find RT, field RT, yeah. So again, this is in the specification number, which is machine readable, all right. In your Python program too, I think you can see that that was the D format there, all right. So the next, okay. So the next bit in the puzzle was to actually convert this pseudo code into executable Python, all right. For that, I decided I absolutely love Python Play, all right. So I recovered the GardenSneaky example, which is a subset of Python and I fixed several of its errors. Ply is an LALR parser based on Yak and Flex, a Yak, Flex, Bison. But Python, the actual language itself, Python, doesn't lex properly in a single pass. Turns out that you actually have to do some fixing of some of the indentation. So I did a, in the lex I look for the tokens and add an extra indent token when the opportunity is detected and didn't de-indent and track it. And that way, I was able to turn it into something that can be properly parsed by an LALR parser. Now, there was also a few other things that needed dealing with silent keyword fall through in things. It also skips from blank, it detects blank lines. And also the syntax here, it turns out to be, this syntax turns out to be Python-like. It's not exactly Python, but it's close enough that I was able to adapt the GardenSneak thing. So I changed the tokens rather than colon, if test colon, I used if test then and inserted the token. So that's what this program does is it go, there we go. If token type equals then, then replace it with a colon. And if it's an else, then we want to yield the else keyword and also add in a colon because that makes it a Python syntax. Same with do, et cetera, et cetera. Then, so the next thing was we actually have to have a page reader which reads the syntax of the things and extracts that pseudo code. And these forms, this is actually very important part of what we're doing. Followed by finally an actual parser, which if you're familiar with Python Play, you have the BNF back as now format syntax in the comment field. So fantastic idea. The latest version of Play actually has it as a decorator, which is kind of nicer because then you can then use the comment field for actual comments. And in this way, we end up with some code. Vi decoder, isa, fixed store.py, and it, and what was stw. And ta-da, we end up with an operation. This is what the compiler outputs. All right, so memory sign, a memo sign there, you know, assigns to memory at that address and this, all the, you know, let's get register or zero. Lots of little hacks like this, but it calculates the effective address plus extents, sign, extents, sign, extents, and so on and so on and so on and so on. Plus extents, sign, extents on the immediate. Notice here that the immediate is not actually in here. What we've done is we've done a decorator which injects local variables into the context by actually altering the globals and the locals of the function. Yeah, I know. And then on exit, it will extract anything that's been modified. So in this case, that same function will extract RA from the name space or any other variables that have been modified. So that's how that operates and it creates, so we can write our own simulator now. Let's have a look at one of the, better, yes, okay. So let's have a look at this one now. So here we're actually running the simulator, the Python simulator. So those instructions got decoded in Python by machine reading those formats. So where we have this fields format here. Sorry, sorry, there was a second. So where we have this format here, we match that against the relevant instructions format here. Excuse me. So then we can pass in some initial memory values that we want to do and then run that simulated instruction and assert and compare it against the expected value afterwards. And we do thousands of these tests. Now, to double check this, we weren't confident that we got it right. So what we did was went, well, there's an implementation of power instruction set simulator. It's called QIMU. Why don't we use the machine GDB MI, GDB machine interface to actually connect to QIMU and here's your arguments for the one running this engine or big engine, no graphics thing and started up and then upload the program, the exact same compiled program to QIMU and then upload memory. So do the exact same thing and load some initial memory values and run the program and then check the registers afterwards. And then that way, what we can do is we can do co-simulation side by side of QIMU with our simulator, all from Python. And then we do the exact same thing with the HDL. So we're doing this bootstrap process, which develop a very slow simulator in Python because it's easier to read and read and understand. Then cross reference and check it against QIMU by co-simulating the R simulator versus QIMU. Once we find that that's correct and it can run, we have confidence in our simulator, we go back and run the HDL, the NMEGEN simulations against the NMEGEN simulations against the Python-based simulator, all right? And 1,000 is literally 1,000 subunit tests just to make absolutely sure that we're confident in what it's doing. Now, let's have a look at the add instruction here. Fixpoint load and store, fixpoint move and assist, arithmetic instructions, okay, add, right, brilliant, okay. So here's the add instruction here, which add immediate. All right, let's do the, no, not that one. All right, brilliant, add RT, R-A plus R-B, all right, brilliant, okay, so we divided down into separate pipe lines. So let me show you those, push team, minus one, no, function units. So there's an ALU pipeline branch, condition register div unit, and these are divided down by the profile register. So anything that needed a similar number of read and write registers would put it in the same pipeline. So this is NMEGEN elaborate. I think bear in mind, here's your, here's that operation comp, let's have a look. Here's a switch, your switch statement. Now, it's switching on the instruction type. That instruction type comes from the, let's actually search for op add. Oh, look, there we go, op add. There we go, there's all the op add operations here. All right, so it says, you want to read R-A and R-B operate operations. Notice you remember from the specification, it said it needs R-A and R-B, all right? So this, so, no, it's back, bring it. Op, op, underscore add, here's our version, that's our op immediate. So this is the CSV files, which are read by Python. So that's add IC, add integer with carry instruction. There's that one, dump, dump, minus code 31, that's the equivalent of the thing, so here's the code comment, add instruction, all right? So add R-A to R-B and add destination is R-T. And oh, look, it has, it can do condition registers and output. So switch statement, it's written in Python, it's got comments, duh. So here's your add. So what we're doing here is we're actually doing a 65-bit add. Which allows us to do the carry and then the carry overflow is a 32-bit, 64-bit carry, so that colon minus one, that takes the 65th bit. And here's a calculation for the 32-bit carry overflow, which came from MicroWatt. So sign extend just calls a function and again, the switch statement is if the data length is one thing, so this is for EXTB, EXT half and EXTW, the different instructions. And we end up joining all those together, those pipe lines, et cetera, et cetera, et cetera, I think I'm gonna have to move on quite quickly because we're starting to, I've got quite a lot else to cover. So now, once that's all done, we can then output it as Verilog. So it's my simple issue of Verilog. So again, we can generate a core here with different options because those options are passed in to the program which in a config file, it will, in a configuration object, it will, the whole reason why we're using Python is because we can enable at compile time, this is a compiling into Verilog. Oh, there we go, there's your options, the specification. We can pass this in and then do if reg width equals equals this or if we want the ZICs controller, interrupt controller to be generated, then output certain stuff into the Verilog. So for the LibreSoft 180 core, which went to ASIC, we needed a way to add for SRAMs, 4K, 4K SRAMs, but running the simulations, we didn't want that, so we made it an option here. So there's the Nomejan Verilog convert. Now, once that's converted, we can then run it, run a simulation. And this is compiling that up and off it goes. And now, one of the other things that you need when developing ASIC is you need a JTAG interface. So you don't think, you know, when you just take it for granted, if you've ever done embedded computing development, you just take it for granted that there's going to be a JTAG interface. Well, somebody has to implement that. All right. And very kindly, staff for Hagan from Chip for Makers made a some Nomejan HDL, which implements a fully functioning JTAG TAP interface. And we've been able to think so, we can set the manufacturer ID and then when you put that either into FPGA or ASIC or in this case in a simulation, which I'll show you in a minute, you can get that manufacturer ID back. But one of the other things, wishbone, one of the things we needed was the ability to upload programs over JTAG. So because we're using a wishbone interface as the bus, staff added the ability to connect to the wishbone interface over JTAG. And through this, we're able to actually upload programs even to the simulator. Now, I don't feel familiar with OpenOCD. It's basically a way to manage hardware. But in this case, what we're doing, it was managing the simulation. The simulation. So we've got a JTAG interface operating here and a remote bit-banging interface is connected up. So whilst that is running the BIOS, we've connected over OpenOCD and sent some commands to it. TDI transmitted the data and TDO. It has connected and found, oh, look, there's a manufacturing ID. Well, hey, there's that ID, not one 8FF, which was a certain part number, zero, zero, zero, one. So that's from here. Where is it? Part number, zero, zero, one. And if you convert that, you'll find that that was 0x818FFF. So from here, we can write some scripts and let me see if I can find them. Unless debug, yeah, yeah, VID bug, that's a firmware upload. This is a command, which if I run this, it will actually upload a program using the same test stuff that we did earlier. So a very simple loop program using the same compiler and et cetera, et cetera, to generate instructions. We can pass those compiled programs into over the JTAG interface to the system that we're connecting to, uploading it over Wishbone and remotely controlling it. Now, what's really nice about this is that the exact same program, the JTAG command system can be used on the simulator and the FPGA and the emulator once we extract from the VLSI tools and resimulate the HDL after it's been laid out. And when we come to it, we'll have a series of programs that when we get the chip back, we can actually test these things. And these are exactly the same code, H and every time. So we're checking again and again and again and again at every single level that what we're doing actually works. Now, let me leave that one. Now, so here was the simulator, which was the Verilog code, which is after it was a, let's Libresoc, Libresoc.v. So here was the machine code looking Verilog after that's been on the median outputs from it's HDL. It will create this very grungy looking Verilog for you. We don't actually want to be reading that code. But what we do want to do is we want to hand it over to Coriolis 2. Now Coriolis 2 is the HDL automatic layout tool. And let's have a quick look here at an example program. Now, this is a very, very simple ALU, which is very useful for what we're doing. So let me just quickly explain it. The top-level module has an op, which is a signal field. It has two input signals, A and B, and it has an output of bit width. So these are HDL signals of width, width. And when we declare the class, we want to go, oh, we're declaring it with 16. Fantastic, all right. So now there's an adder module and a subtract module here. Let's have a quick look at those. So the adder module has an A and a B as input, and it has O as an output, and O look in combinatorial logic form. It adds A to B, and that's your output. Go figure, all right. And the subtract one, oh look, that does a subtract of A from B. Wow, all right. So what we then do is, depending on whether op is true or false, on the next clock cycle, the output will be set either to certain results of the subtract or to the results of the add. Now, I want to just think, what I've found is that it is incredibly important to view these things graphically. I didn't do gate-level design or hardware. Things, I learnt this on the fly, all right. So here's your input A and B, which is part A and B are passed to the adder and subtractor modules. The outputs come from here, and then there's depending on the op here, it will do, it will produce that output. Now, if we do proc opt synth, now I don't normally do this, but I did want to show it to you because it shows you right down to the gate level. Now, what this has done is, it has taken that the cell library and gone down to individual cells. So it's split out the bits of A and B, received at the output, and where is op? Yes, op goes into each individual mux here and select one in each of the bits of A and B, and you don't normally see this kind of thing when you're dealing with high-level stuff. And then it goes into a flip-flop to capture that output from the mux, so that it's stable for the, so it will be available for the next clock cycle. And then each output, bit zero, of each one gets put together and it goes into the output here, okay. All right. Now, here's one I prepared earlier, all right. It's, this is what the actual chip looks like when it's done the layout, okay. Whoops, I always get that wrong. Ha ha. And here's you into individual transistors within the cells, okay. Now, in this particular program, what I did was I'm, and this is a beautiful thing about Coriolis 2. I did a manual place of the adder on the left side and add a manual place of the subtractor on the right. And then I did a manual place on the individual components of the cells in the muxes. And then, and from there, I asked it to do the routing. Ask Coriolis to handle the routing. But all of this layout here was done, I specified these locations of where I wanted this stuff to be in Python and I specified in a Python program where I wanted the add to be, add block to be and subtract block to be. Now, you can't do this kind of thing without having some kind of verification. So what we did was a part of Coriolis 2, what it does is it extracts, once it's done this layout here, it re-extracts the net list for you into a subset of VHDL. So what we then did was passed that VHDL over to a CoCoTB simulation, which I've just got to find. Right, CoCoTB. CoCoTB is similar to what we're doing inside the testing except what it does is it takes, where Nomejan has its own simulator and you can run a side by side simulation of HDL versus some Python code. CoCoTB does the same thing, the exact same thing for Verilog and VHDL. And so consequently, when we have this VHDL here, we can put it back into CoCoTB and simulate it and own look, we can run the exact same JTAG tests that we did earlier, including uploading over Wishbone, sending Wishbone commands. This is, it apologizes, it's quite hard to understand, but here's the command for read and write because that saves things. So we're writing the Wishbone address here. We want to write to data address 00001 and then we want to write to memory and then read back again. So we write the binary data here and then read back and then do it as an assert to make sure that the result, standard Python assert, to make sure that the result comes back is what we expected. So that's basically what, I'm terribly sorry, I'm running out of time for that in the talk length. So just have to go briefly over these other things. One of the things that we're also doing is implementing instructions. So we're doing our own advanced instructions, adding those tests draft to the power instruction set because that's the funding that we got from NL to do this. And so the one I'm currently doing is discrete cosine transform which start off from the original code, the best implementation I'll be able to find is a recursive line of project Naoki. I then turned that into an iterative variant and then used Python iterators to express the triple loop that this is in. The DCT is usually expressed recursively. I tried to create an iterative version that has three triple loops and then I created some Python generators which generate those so that we can use them in the simulator and in the HDL and check the things that are going on. Anyway, very briefly, if you'd like to help we do actually have funding available of things in several different areas. This is one serious computer science project mostly written in Python that NLnet are very kindly been supporting us in this endeavor. So that's Coriolis 2. We have a script for an automated install of it so you don't have to do the mess about trying to work out how to get it up and done. It will create a Debian S charoot in Debian 10 for you. And again, that's the Chips of Chips will make us website if you want to do your own ASIC, he will help you and he's also a Python programmer. So all right, so yeah, if you'd like to help his procedure, so yeah, well, thank you very much for your time. Hello again, that is the end of the recorded portion of this talk session. I would like to now invite Luke into the studio to answer any questions though. As many of you in element will have noticed Luke has been prolific in answering questions and chat and providing extra resources and context at the top. It was just, so thank you. That was really informative, a little bit over my head just because I haven't done any embedded programming of any kind in my life, but wow. It's pretty heavy going. It's, you just have to be extraordinarily patient with this level of detail. It's using Python to actually emulate and implement hardware, which is, yeah. I have a question and this is super basic, but at least one other person who's attended this talk is in the same position. How did it get started with this? It helps that I was using my first computer's age seven in 1977, so I've been programming for 43 years. And I was messing about circuit boards, PCBs, when I was like 12, things like that, soldering out and all sorts of stuff. So I kind of got the background, but you just make the decision, this is what I'm gonna do. I had no idea what I was doing and to be honest, I still don't. But from the software engineering training, engineering training, the rule of SD, as an engineer was taught, before you start, always know what the answer is going to be. That way you can write yourself a term, look for what you're doing. And so that's what I've been doing. And just very patiently, drilling down fractal rabbit holes in effect. One after the other, just very, very patiently and learning what's needed as a go along. That makes a lot of sense. Well, while we're waiting to see if anybody has a specific question chat, I understand that this is like a rabbit hole in itself to ask about, but I have a security background. So like, whenever I end up using a tool, I end up having to do quite a lot of research into figuring out if it's secure or not. Package, auditing as an entire nightmare. Are you running entirely Libre software and hardware? Yes, yes, I've got a Versa ECP-5 here and we also, one of our developers has a ULX 3S. The ECP-5 was reverse-engineered. So the tool chain for that is next PNR-ECP-5. And it's entirely Libre licensed, ACP-5. So when we're even doing the FPGA testing, we don't have to use installed Xilinx proprietary tools. Oh, right. Okay, that makes sense. I guess kind of like your knowledge of hardware and processors. I assume that this is the kind of thing that you've just sort of built up and accumulated over time because I don't even know where I would begin to shift to that kind of tool chain. It's combining us as a series of projects, things, you know, but it helps that I did electronics when I was younger. And I've done embedded design, you know, so I've missed about with ARM boards. I've done reverse engineering. I did bring up on XDA developers in 2003. I was using the Hiret or the GNU Hiret handheld reverse engineering tool to actually boot our own versions of Linux on wind smartphones. And so the kinds of things that we need for bootstrapping up the hardware, I know where I'm going, and it's a sort of a road map. So it's really strange. I just decided one day I was going to do this, but it turns out that everything that I'd done up to that point was relevant training for this project. It's quite fortuitous. That makes sense. There was a lot of transferrable knowledge. For me, just from my level of experience and the kind of things I've worked on, I have what I feel is like absolutely no transferrable knowledge to get involved with this kind of work. Well, I mean, later on, we'll actually have processes that we can do but I mean, later on, we'll actually have processes up and running on FPGAs, you know, big FPGAs where we will need people to do, you know, security audits of the thing, you know, actually running programs and seeing if it's resistant to spectra and meltdown and blah, blah, blah, et cetera, et cetera. But the nice thing is, by that point, you will actually be able to inspect the actual gate level running of the things and say, this is a problem here and highlighting it for us and somebody can come along and fix it. Can you imagine doing that with Intel? No, no chance. If you've controlled everything about this piece of hardware and you can audit it to a level of just like technical sophistication depth that you wouldn't otherwise be able to with a commercial board, for example. Yeah, exactly. And the important thing about that is then that did you hear that Supermicro got delisted from the NASDAQ stock exchange for being unable to prove the provenance of their components? I did actually read about that. Yeah, if they do start a process or use the techniques like it, they would have been able to say, knock yourselves out, go and have a look at the HDL. Huawei would have been able to take the U.S. government to court over the removal of the things because they'd be able to say no, Mr. Trump. Here's the under escrow license. You can actually expect the HDL. But you can't even do that with an Intel processor right now or an AMD or an ARM or anything because they licensed third party macros. Wow. Intel will license about 60 different pieces of HDL for subcomponents where even if you had a customer come along with several million dollars saying, here's a water cache, I want to look at the source code, they'd hit a wall of a thousand lawyers from third party IP. Right, and that impedes any further innovation in the space. Basically, yes. Wow. Yeah, so we're actually sort of smashing open a whole stack of doors of what's it called apathy. We're not just using this in Python. We're actually giving free software developers the opportunity to contribute to something that's part of the future of computing. It's a fantastic project to be part of. Yeah, it's pretty awesome. I'm delighted. It was 650,000 euros in funding from NLNet and from NGI pointers. So if anybody wants to help out, they're more than welcome to do so. Yeah, we've essentially run out of time and there will be a 10 minute break, but I feel like you've covered every possible question anybody could have possibly come up with. So thank you for giving this talk. If people want to follow up, there is a breakout room available that they can continue to ask questions in if you're available to answer anything else. But beyond that, it's because I feel like this is something people would want to do. Do you have a preferred medium for communication? Can you be reached through Twitter or email or something? We're under audit conditions with NLNet, so private conversations will find no problem, but for technical discussions and et cetera, we have to use the IRC channel, which is logged and mailing list on lists.librestock.org. All resources and everything, if you just go to Librestock.org or we're pretty high up on the search engines now for just type in Libre-soc and you'll find us. All right. Thanks, Luke. That was an incredible talk. I learned a lot. I feel like it's going to take a few months before it all settles in. Yeah. And yeah, for everybody who is still currently in this room, we will now be taking a 10-minute break. I think we're at about the halfway mark for the conference. Following that break, you will have a new session chair in this room. The next talk will be the spec you never knew you needed and it will be the next session chair's responsibility to give the pits for all of those. So thank you all for attending this talk. Once again, Luke, thank you for delivering it and I hope you have a good afternoon.