Abstract and Bio
Learn how computers work between silicon and assembly — Build a CPU with Python��2018-12-11 @ CARPE Dec Meeting, Columbus, Ohio��Programming languages are designed for a specific level of abstraction or distance from the hardware. The main trade off is "developer productivity" vs "control over hardware". C and assembly are low level and therefore map closely to CPU instructions. Python on the other hand goes through many layers, libraries, and a virtual machine before the CPU is reached. This allows powerful programs to be written concisely and cross-platform—but it also conceals the true nature at the heart of our modern world. Unveiling the magic within can lead to interesting insights about how computing got to where it is today.��We will build up CPU focused on transparency, interactivity, and modularity. Our CPU has a configurable architecture and machine language. Yes, you can invent your own assembly instructors to add functionality. We will cover registers, data/address busses, memory (ROM/RAM), IO, and assemblers.��Specs for nerds: 8-bit words, 256 memory addresses, Von Neumann w/ shared address+data bus, DMA with numpy based buffer.��Bio:�Zak Kohler is a Chemical Engineer by education but true computer geek at heart. He started programming in 3rd grade and has never looked back. Often zak states that his biggest regret is not finding Python and Linux sooner...that didn't happen until university. Electronics is his second love and he fuses the two by playing with early computer hardware as well as modern microcontrollers. When zak isn't messing with computers he can be found turning milk into cheese, drawing, or exploring the world on long walks. �
Setup and Play Along
$ git clone https://y2kbugger@bitbucket.org/y2kbugger/SAPy.git�$ cd SAPy
$ pipenv install --dev # *note
$ pipenv shell # Will launch shell (e.g. bash) in venv
(SAPy) $ jupyter notebook
Python 3.7 !!
If you don't have pipenv, install it using your package manager as a user pip package:
$ pacman -S python-pipenv # arch linux
$ brew install pipenv # homebrew on osx
$ pip install --user pipenv # pip
$ conda install pipenv # windows/anaconda
zak@zakkohler.com
Learn how computers work between silicon and assembly
Build a CPU with Python
2018-10-29 zak Kohler
Zak Kohler
(y2kbugger)
Grey Title
Normal text
Blue Text box
Useful Shortcuts/Bash Review
Definition : <Ctrl>
A Long Text with code below:
$ pkg install python
$ pip install pip --upgrade
Note on PIP_REQUIRE_VIRTUALENV https://stackoverflow.com/questions/27410821/how-to-prevent-pip-install-without-virtualenv
Intermediate Section
(with a subheading, or aside)
$ vim fun.py
run with <F5> (VolUp + 5)
1 import requests¬
2 req = requests.get("http://cohpy.org")¬
3 print(req.text)¬
~
~
~
~
~
~
~
Motivation
Abstraction
Simple interfaces allow complex systems
You don't think about radios each time you send a text via sms,
this is normally considered a good thing.
Video Series on Computer Science "Up another level of abstraction"
Abstractions in Computers
Applications
Operating System
Physical Silicon Layout
Circuit Schematic
Digital Logic
Datapath
Microcode
IO Systems
CPU / Instruction Set Architecture
Firmware
Compiler
In Real Life Silicon
Hardware
Software
Abstractions in
Programming Languages
Image: http://carlcheo.com/compsci
Abstraction in electrical engineering
Programmers using libraries of code with Application Programming Interfaces (APIs).
Electrical engineers design using a library of parts with specified electrical interfaces.
Wikipedia: Lumped Element Model
There was still some magic
Applications
Operating System
Physical Silicon Layout
Circuit Schematic
Digital Logic
Datapath
Microcode
IO Systems
Instruction Set Architecture
Firmware
Compiler
In Real Life Silicon
Hardware
Software
In pursuit of killing the magic I've been playing with hardware recently
This talk is a step towards building a IRL CPU
Outline
Hex/Binary
Hex values 0, 1, ..., E, F are equal to decimal 0, 1, ..., 14, 15
15 == 0xF == 0b1111
The maximum value for 1 Byte or 8 bits is
255 == 0xFF == 0b1111_1111
Hex is also show using $, e.g. 0xFF == $FF
Example of C-like --> Assembly
main()
{
int a=4;
int x=3;
while(1)
{
a += x
putchar(a)
}
}
0x00 LDA $07
0x02 ADD $08
0x04 OTA ; Output a
0x05 JMP $02 ; loop back
0x07 $04 ; a
0x08 $03 ; x
Assembly Language
Can basically translate directly to machine code
Assembly/machine code is specific to a specific CPU architecture, e.g. x86, ARM, SAPy-16
Allows you to use Mnemonics instead of numbers.
e.g.
LDA #$07 vs 00100000 00000111
Machine Code Execution
0x00 LDA $0A�0x02 ADD $0B�0x04 DMA�0x05 STA $09�0x07 JMP $02
0x09 $00
0x0A $99
0x0B $0E
LDA
#0A
ADD
$0B
DMA
STA
$4A
JMP
$02
$00
$99
$0E
0x00
0x01
0x02
0x03
0x04
0x05
0x06
0x07
0x08
0x09
0x0A
0x0B
Jump
STA Target
LDA/ADD Targets
Our Virtual Machine: based on SAP-1 (Simple As Possible)
Digital Computer Electronics
By: Albert Paul Malvino
The Ben Eater SAP-1
Show Video
Ben Eater "8-bit computer update"
SAPy-16 Instructions
Binary: AAAA BBBB [CCCCCCCC]�HEX: AB [CC]
A - Addressing Mode; e.g. Absolute, Immediate�B - Operator; e.g. Load value into A Register�C - Operand, value or address of value; e.g. $D7
* Not all instructions have a operand
�
SAPy-16 Instructions
LDA - Load value into Accumulator
STA - Store Accumulator to Memory�OUT - Output from Memory�ADD - Add to Accumulator
SUB - Subtract from Accumulator��JMP - Jump execution to somewhere else�
DMA - Allow DMA controller to take over bus
HLT - Halt processing�NOP - Do Nothing�OTA - Output Accumulator�
�
Clocking Signals
component.data() # Rising Edge, returns data for a clock cycle.
component.clock() # Falling Edge, get passed data from that was fetched on the rising edge of the clock cycle.
Buses & Registers
System bus
SAPy Buses & Registers
8
B
I
T
B
U
S
8
8
8
Accumulator (Reg A)
□□□□ □□□□
Output Register (Reg O)
□□□□ □□□□
LA
CLK
CLK
LO
EA
Shared data and address bus
Control bus
Register Transfer
Goal:
Move byte around CPU
Requirements:
- Exactly one "enable"
- One or more "latching"
E.g. OTA
Take how the controls are controlled for granted, for now
8
B
I
T
B
U
S
8
8
8
Accumulator (Reg A)
□□□□ □□□□
Output Register (Reg O)
□□□□ □□□□
LA
CLK
CLK
LO
EA
Program Counter
The program counter (PC) tracks of where in memory the next instruction to execute is.
The PC is incremented as each instruction is fetched.
To execute an instruction other than the next sequential one in memory, you can set the PC to an arbitrary memory address, this allows loops and subroutines.
8
B
I
T
B
U
S
8
8
Program Counter (PC)
□□□□ □□□□
CP
CLK
EP
LP
Accumulator (Register A)
>>> reg_a = RegisterA()
>>> reg_a.data(ea=True)
0
>>> reg_a.clock(data=0xAB, la=True)
>>> reg_a.data(ea=True)
171
8
B
I
T
B
U
S
8
8
Accumulator (Reg A)
□□□□ □□□□
CLK
EA
LA
Arithmetic Logic Unit
8
B
I
T
B
U
S
8
8
Accumulator (Reg A)
□□□□ □□□□
CLK
EA
LA
8
Register B
□□□□ □□□□
CLK
LB
8
ALU
□□□□ □□□□
EU
SU
8
8
Memory
Think of ram as a bunch of registers. There is a special circuit that turns an address into an enable for the correct register.
8
B
I
T
B
U
S
8
Memory Address Reg (MAR)
□□□□ □□□□
CLK
LM
8
RAM 16 x 8 bit
0x00 □□□□ □□□□
0x01 □□□□ □□□□
0x02 □□□□ □□□□
0x03 □□□□ □□□□
...
0xFF □□□□ □□□□
CLK
ER
LR
8
ER 0x00
ER 0x01
ER 0x02
ER 0x03
...
ER 0xFE
ER 0xFF
MAR
□□□□ □□□□⬜
8
Address Decoder
↑
256 Registers
↓
Controller
Sequencer
Each instruction takes 6 clock cycles. T-State cycles 1 through 6.
Decoder
For each combination of opcode and T-state a specific control word is defined
8
B
I
T
B
U
S
Instruction Register
□□□□ □□□□
CLK
EI
LI
13
Sequencer/Decoder
T: □
CLK
8
8
addressmode
opcode | T | LA | EA | LM | LR | ER | LI | ... | EO |
0000 | 1 | | | | | | | ... | |
... | | ... | ... | ... | ... | ... | ... | ... | ... |
1111 | 6 | | | | | | | ... | |
Control Word
LA, EA, LM, LR, ER, LI, ..., EO
opcode
Output (Register O)
reg_o = RegisterOutput()
reg_o.clock(data=0xAB, lo=True)�
8
B
I
T
B
U
S
8
Output Register (Reg O)
□□□□ □□□□
CLK
LO
Example Sequencing: LDA $D4
Fetch Cycle, always the same:
T1: ('ep', 'lm', 'cp')� T2: ('er', 'li')�
...DECODE BASED ON Instruction Register...
Execution Cycle, based on decoding:
Absolute addressing:�T3:('ep', 'lm', 'cp')�T4:('er', 'lm')
STA:
T5:('er', 'la')
Memory Mapped vs Port Mapped IO
Operating Systems
First operating system: Human operators
Abstracting Hardware
The job of an operating system is to abstract away hardware. They do this by implementing standard interface for tasks that talk to real world.
Timesharing
Imagine if this guy had to manually set the program counter each time a human interrupted with some input...or change out one job for another thousands of times per second.
Subtleties of Performance
IO vs CPU bottleneck
Quirks of Legacy - Strings
Try thinking of hardware when you are wondering:
"Why would anyone ever do it like this?"
Security
Let's review the implementation
Further Learning
Topics Not Covered
Others
Ben Eater - >40 videos, building on breadboard�Crash Course Computer Science - Very approachable
Email for endless youtube links on related things: