| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Hiring is hard, a lot of modern CS education is really bad, and it's hard to find people who understand the modern computer stack from first principles.
Now cleaned up and going to be software only. Closer to being real.
So about those transistors -- Course overview. Describe how FPGAs are buildable using transistors, and that ICs are just collections of transistors in a nice reliable package. Understand the LUTs and stuff. Talk briefly about the theory of transistors, but all projects must build on each other so we can’t build one.
Before the transistor
Vaccum tubes
Use heat turn circuits on or off
Transistors
Electricity
The motion of electrons through charged semiconductant materials
Silicon
Semiconductor with 4 electrons on its outer energy level; is able to form a stable crystal lattice made of four-way covalent bonds when not altered
Diodes
Allows current to flow in one direction if a certain voltage is reached. Consists of an anode, cathode, and depletion layer between them.
Doping Silicon - Introducing Charge
Depletion Layer / Dead Zone
NPN Bipolar junction transistor type transistor
What are Integrated Circuits?
Very small circuits made from other smaller circuits made out of discrete logic components made out of silicon
How are Integrated Circuits made?
Boolean logic - Math? Function with binary input, binary output. Ex: fn = AND(one: 0|1, two: 0|1) -> out: 0|1
Logic gates Implementation of boolean logic from discrete logic units (transistors and such). Transistors can act as switches, which can be on or off. We can use boolean gates to create boolean logic (ex AND, OR, XOR).
FPGA - Field Programmable Gate Array
CLB - Configurable Logic Block
Logic cell Contains Flip flops, a full-adder and LUTs
Full-adder Takes in two binary inputs, each one bit, and a carry input, outputs the sum of the binary inputs, and the carry bit.
LUTs - Lookup tables Implentation of Boolean Gates (AND|OR|...) using muxes that act as truth tables. We hardcode the output for specific inputs.
Register/Flip flop Record the value of input every clock cycle. Used to "record" state.
Microcontroller
Transistor Theory - https://backyardbrains.com/experiments/transistorTheory#prettyPhoto
Transistor Circuit Design - https://backyardbrains.com/experiments/transistorDesign
Emulation -- Building on real hardware limits the reach of this course. Using something like Verilator will allow anyone with a computer to play.
Blinking an LED(Verilog, 10) -- Your first little program! Getting the simulator working. Learning Verilog.
Look at implementation
Building a UART(Verilog, 100) -- An intro chapter to Verilog, copy a real UART, introducing the concept of MMIO, though the serial port may be semihosting. Serial test echo program and led control.
Look at implementation
Coding an assembler(Python, 500) -- Straightforward and boring, write in python. Happens in parallel with the CPU building. Teaches you ARM assembly. Initially outputs just binary files, but changed when you write a linker.
Input: ARM assembly instructions file Example: // hello.arm mov r1, #7 mov r2, r5 cmp r2, #5 ldr r2, r5 addlt r0, r0, #1 Output: File with a binary instruction in each line 1110 00 1 1101 0 0000 0001 000000000111 1110 00 0 1101 0 0000 0010 000000000101 1110 00 1 1010 1 0010 0000 000000000101 1110 010 0 0 0 0 1 0101 0010 000000000000 1011 001 0 1 0 0 0 0000 0000 000000000001
Stack pointer: points to top of stack. every memory address holds a byte
0x01 n 0x02 s 0x03 fourth <- top of stack 0x04 third 0x05 second 0x05 first stack grows from high address, to lower address
Questions:
Used for allocating memory dynamically: variables. The heap is created as the application starts. In the application's runtime, when a variable gets created it is placed in the heap.
Independent execution contexts of a process that have their own stack and share the same memory as the process.
When using bl branch instructions, the link register keeps track of the next instruction. We need it so when a function finishes, we execute the instruction after the function call, just like we expect with modern progarmming languages.
Example usage:
.global: _start
_start:
// instructions...
bl func
// next instructions
func:
// instructions
bx lr // go to instruction after the branch started
Current Program Status Register (CPSR): Holds state of last excecution. Example: whether there was a carry bit, negative value, if we're in privileged mode, ...
ARM VS Thumb (arm state) ARM:
Assembler: assembly file -> assembler -> machine code
Example:
mov r0, #5 -> 0101010101....
Binary: 10101011 10101111 000001010110010010 # Numbers depend on ARM asm spec
________|________|________________
Symbolic (asm): LOAD R3 7
Symbol = Variable | Label | pre-defined symbol (operator, mnemonic, registers) Each symbol is mapped to a memory address specified in Symbol Table
Label: maps to next intruction memory adderss Variable: each new variable assembler looks at gets assigned higher memory address
a 32-bit instruction in every line
Instructions: A-instruction B-instruction
Example:
01011100010111000101110001011100 01011100010111000101110001011100
Makes programs executable. It has instructions for loading code from different files, and shared libraries, and allocating data memory for each of them. It connects your program code, to library code.
Static memory: created at compile time Dynamic memory: created at run time
Staic linking: ELF file contains the code for the shared libraries Dynamic linking: Resolves code for shared libraries at run time
Relocation: Resolve function calls to code. Used during linking
Segments: Sets aside memory for code and data for the program, as well as for shared libraries. Sections: Used during linktime
How does the asm know which memory address a pre-defined symbol (mnemonic/register/operator) belongs to? The assembly spec (ARMv7 spec) speficies a number for every pre-defined symbol. These values are initiliazed when the assembler start
hypothetical symbol table ----------- mov | 0x1 add | 0x2 sub | 0x3 ... |
How does the asm know which memory address a label belongs to? In the first pass, the asm maps the label name, to the next instruction memory address in the Symbolic Table Example
label: 0x0 add 5, 2 0x1 symbol table ----------- label | 0x1 ... |
How does the asm know which memory address a variable belongs to? In the first pass, the asm looks for variable declarations, and maps a memory address for it in the Symbolic Table. In the second pass, asm replaces all variables with memory address in Symbolic Table. Example
var1= 0 var2= 0 symbol table ----------- var1 | 1024 var2 | 1025 ... |
def assembler:
bin_instructions = []
for line in file: #line is a command (or whitespace/comment)
fields = parse_fields(line) # 'LOAD r1, 7' -> [LOAD, r1, 7]
command_bin_codes = []
for field in fields:
bin_code = field_binary_code(field) # LOAD -> 0101010110
cmd_bin_codes.add(bin_code)
cmd_bin_instructions = parse_bin_codes(command_bin_codes) #list of 32-bit binaries
bin_instructions.add(cmd_bin_instructions)
asm_bin_instructions = assemble_bin_codes(bin_instructions)
file.write('asm.o', asm_bin_instructions)
Implmentation overview:
When reading PC while debugging, PC will point to two instructions ahead. This is old behavior that is maintained to ensure compatability. Carry occurs if result of a subtraction is >= 0
Program counter: current instruction memory address plus word length (8 in a 32-bit arch) during branch: holds destination address
What's up with arm storing first four argruments of a function in first four registers? Only four arguments can be stored at a time? Is a functino an arm instruction?
Building a ARM7 CPU(Verilog, 1500) -- Break this into subchapters. A simple pipeline to start, decode, fetch, execute. How much BRAM do we have? We need at least 1MB, DDR would be hard I think, maybe an SRAM. Simulatable and synthesizable.
Notes:
BRAM - Block RAM. This is available in FPGAs
RAM - Random Access Memory
SRAM - Static RAM
DRAM - Dynamic RAM
DDR RAM - Fancy modern DRAM
Data and instructions are stored in same block of memory
What happens when you give the CPU an instruction? Simple version
Considerations
ROM chip Input: select_memory_address Output: *select_memory_address Implementation: Big-ass mux outputs the value of a selected register. Holds one million registers in the case of a 1MB ROM chip. MUX 000101
Fetch instruction chip Input: instruction memory address Output: 32-bit binary instruction
Decode chip Input: 32-bit binary instruction Output: opcode/mnemonic, register_a, register_b, register_c, memory_address_a, memory_address_b, memory_address_c, imm_value_a, imm_value_b, condition
Execute chip Input: opcode/mnemonic, register_a, register_b, register_c, memory_address_a, memory_address_b, memory_address_c, imm_value_a, imm_value_b, condition Output: next_instruction
RAM Chip Contains data (and instructions in a Von neumon architecture) Input: select_memory_address, should_write, value Output: RAM[select_memory_address]
Notes
Coding a bootrom(Assembler, 40) -- This allows code download into RAM over the serial port, and is baked into the FPGA image. Cute test programs run on this.
References
Building a C compiler(Haskell, 2000) -- A bit more interesting, cover the basics of compiler design. Write in haskell. Write a parser. Break this into subchapters. Outputs ARM assembly.
Overview: Tokenize -> Parse -> Build AST -> Generate ARM
Compiler explanation High level overview: source code -> executable Compilation outside of programming?: structure text, act on it. Before compilation, program (code) is nothing more than a list of characters We need a way to structure these characters, make sense of them, and execute them
/* programmer code name = "john" print("hello", name) / -> / arm code name string mov "john", name ... */ Compiler needs to transform the source code into something useful, executable. In this case, executable is ASM (assembly) code
assembler asm: mov r1, #5 -> bin: 0100110101001101
Compilation steps:
Tokenize Source code -> List of tokens Ex: 'var name = "hello" ' -> [Token{Type: 'identifier', literal: 'name'}, Token{Type: 'equals', literal: '='}, ...]
Build AST Ex: [Token{Type: 'identifier', literal: 'name'}, Token{Type: 'equals', literal: '='}, ...] ->
Something like this??? Root{ FunctionCall( Expression(Literal: 4), Expression( Operation( Type: SUM, A: Expression(Literal: 4), B: Expression(Literal: 10) ) ) Let's figure out the structure of an AST...
Transform AST
Generate ASM
| Back | FazBrowse Home | New Git URL |