STUDIO HAZE · jeniapp
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20.2 · 63 files
apps/circuit/chips/seq.tpz
Topaz · 30750 B · Original file
1
// 순차 칩 라이브러리. 교차결합 NAND 래치부터 레지스터, 카운터, 램, 롬, 프로그램 카운터, 작은 처리장치까지 칩 정의 글로 둡니다.2
// 입력은 없고 출력은 정의 순서대로 이은 글입니다. main 이 library seq 와 library cpu 에서 회로 앞에 붙입니다.3
// 늘 같은 글을 냅니다. 게이트를 엮은 회로 글일 뿐이고 되먹임은 엔진의 단위 지연 정착이 풉니다.4
// 회로를 평가하지 않습니다. 상수는 배선으로만 만들고 호스트 상태를 쓰지 않습니다.5
// Sequential chip library: latches, registers, counter, RAM, ROM, program counter and a tiny CPU as chip definitions.7
// 칩 정의 글의 줄은 아래 다섯 꼴 가운데 하나이고 늘 줄바꿈으로 끝납니다.8
function 노드(아이디: string, 종류: string, x: int, y: int) -> string {9
"node {아이디} {종류} {x} {y}\n"10
}12
function 칩(아이디: string, 칩이름: string, x: int, y: int) -> string {13
"chip {아이디} {칩이름} {x} {y}\n"14
}16
function 전선(출발: string, 도착: string) -> string {17
"wire {출발} {도착}\n"18
}20
function 바깥입력(핀: string, 노드아이디: string) -> string {21
"extin {핀} {노드아이디}\n"22
}24
function 바깥출력(핀: string, 노드아이디: string) -> string {25
"extout {핀} {노드아이디}\n"26
}28
function 이어붙이기(줄묶음들: Array<Array<string>>) -> Array<string> {29
[for 묶음 in 줄묶음들 for 줄 in 묶음 => 줄]30
}32
// 노드 순서가 정착 상태 문자열의 순서이므로 줄 묶음은 적은 순서 그대로 잇습니다.33
function 칩정의(칩이름: string, 줄묶음들: Array<Array<string>>) -> string {34
"chipdef {칩이름}\n" + 이어붙이기(줄묶음들).join("") + "endchip\n"35
}37
// 비트 연산이 없어 2 의 거듭제곱으로 나눈 나머지로 봅니다. 토파즈 과제 '비트 연산' 의 우회입니다.38
function 비트인가(값: int, 자리: int) -> bool {39
(값 / reduce(0..<자리, 1, (곱, i) => 곱 * 2)) % 2 == 140
}42
// 디코더 출력 k 의 입력 비트는 k 의 그 비트가 1 이면 입력 그대로, 0 이면 그 입력의 NOT 입니다.43
function 리터럴(입력접두: string, 부정접두: string, k: int, 자리: int) -> string {44
if 비트인가(k, 자리) { "{입력접두}{자리}:out" } else { "{부정접두}{자리}:out" }45
}47
// 입력은 활성 로우라 nS 가 0 이면 셋, nR 이 0 이면 리셋, 둘 다 1 이면 직전 상태를 지킵니다.48
// 지키는 상태는 되먹임 고리라 위상 평가로는 풀리지 않고 정착 평가가 풉니다.49
function 셋리셋래치() -> string {50
"chipdef srlatch\nnode si INPUT 0 0\nnode ri INPUT 0 80\nnode q NAND 90 10\nnode nq NAND 90 70\nnode qo OUTPUT 180 10\nnode nqo OUTPUT 180 70\nwire si:out q:in0\nwire nq:out q:in1\nwire ri:out nq:in0\nwire q:out nq:in1\nwire q:out qo:in0\nwire nq:out nqo:in0\nextin nS si\nextin nR ri\nextout Q qo\nextout Qb nqo\nendchip\n"51
}53
// E 가 1 이면 Q 가 D 를 따르고 0 이면 직전 Q 를 지킵니다. NAND(D,E) 와 NAND(NOT D,E) 로 셋리셋 래치를 구동합니다.54
function 데이터래치() -> string {55
"chipdef dlatch\nnode di INPUT 0 20\nnode ei INPUT 0 120\nnode nd NAND 90 30\nnode sb NAND 90 80\nnode rb NAND 180 110\nchip lat srlatch 270 60\nnode qo OUTPUT 380 40\nnode nqo OUTPUT 380 100\nwire di:out nd:in0\nwire di:out nd:in1\nwire di:out sb:in0\nwire ei:out sb:in1\nwire nd:out rb:in0\nwire ei:out rb:in1\nwire sb:out lat:nS\nwire rb:out lat:nR\nwire lat:Q qo:in0\nwire lat:Qb nqo:in0\nextin D di\nextin E ei\nextout Q qo\nextout Qb nqo\nendchip\n"56
}58
// 마스터는 NOT CLK, 슬레이브는 CLK 로 여는 래치 둘이라 CLK 상승 에지에서만 D 를 잡습니다.59
function 데이터플립플롭() -> string {60
"chipdef dff\nnode di INPUT 0 20\nnode ci INPUT 0 140\nnode nc NAND 90 150\nchip m dlatch 180 20\nchip s dlatch 320 20\nnode qo OUTPUT 440 40\nnode nqo OUTPUT 440 100\nwire ci:out nc:in0\nwire ci:out nc:in1\nwire di:out m:D\nwire nc:out m:E\nwire m:Q s:D\nwire ci:out s:E\nwire s:Q qo:in0\nwire s:Qb nqo:in0\nextin D di\nextin CLK ci\nextout Q qo\nextout Qb nqo\nendchip\n"61
}63
// RST 가 1 이면 셋을 막고 리셋을 걸어 Q 가 0 으로 하나뿐인 정착점에 갑니다. 그래서 처음 켤 때도 발진하지 않습니다.64
function 초기화셋리셋래치() -> string {65
"chipdef srlatchr\nnode si INPUT 0 0\nnode ri INPUT 0 60\nnode rsti INPUT 0 120\nnode notrst NAND 80 120\nnode nns NAND 80 0\nnode nseff NAND 160 20\nnode rt NAND 160 80\nnode reff NAND 240 80\nnode q NAND 320 20\nnode nq NAND 320 90\nnode qo OUTPUT 410 20\nnode nqo OUTPUT 410 90\nwire rsti:out notrst:in0\nwire rsti:out notrst:in1\nwire si:out nns:in0\nwire si:out nns:in1\nwire nns:out nseff:in0\nwire notrst:out nseff:in1\nwire ri:out rt:in0\nwire notrst:out rt:in1\nwire rt:out reff:in0\nwire rt:out reff:in1\nwire nseff:out q:in0\nwire nq:out q:in1\nwire reff:out nq:in0\nwire q:out nq:in1\nwire q:out qo:in0\nwire nq:out nqo:in0\nextin nS si\nextin nR ri\nextin RST rsti\nextout Q qo\nextout Qb nqo\nendchip\n"66
}68
// 데이터래치와 같고 초기화 셋리셋 래치에 RST 를 그대로 넘깁니다.69
function 초기화데이터래치() -> string {70
"chipdef dlatchr\nnode di INPUT 0 0\nnode ei INPUT 0 80\nnode rsti INPUT 0 160\nnode nd NAND 80 0\nnode sb NAND 80 60\nnode rb NAND 160 100\nchip lat srlatchr 250 70\nnode qo OUTPUT 380 40\nnode nqo OUTPUT 380 110\nwire di:out nd:in0\nwire di:out nd:in1\nwire di:out sb:in0\nwire ei:out sb:in1\nwire nd:out rb:in0\nwire ei:out rb:in1\nwire sb:out lat:nS\nwire rb:out lat:nR\nwire rsti:out lat:RST\nwire lat:Q qo:in0\nwire lat:Qb nqo:in0\nextin D di\nextin E ei\nextin RST rsti\nextout Q qo\nextout Qb nqo\nendchip\n"71
}73
// RST 가 1 이면 두 래치가 0 으로 정착해 클럭 없이도 Q 가 0 으로 정해집니다.74
function 초기화플립플롭() -> string {75
"chipdef dffr\nnode di INPUT 0 0\nnode ci INPUT 0 100\nnode rsti INPUT 0 200\nnode nc NAND 90 100\nchip m dlatchr 180 0\nchip s dlatchr 330 0\nnode qo OUTPUT 460 40\nnode nqo OUTPUT 460 110\nwire ci:out nc:in0\nwire ci:out nc:in1\nwire di:out m:D\nwire nc:out m:E\nwire rsti:out m:RST\nwire m:Q s:D\nwire ci:out s:E\nwire rsti:out s:RST\nwire s:Q qo:in0\nwire s:Qb nqo:in0\nextin D di\nextin CLK ci\nextin RST rsti\nextout Q qo\nextout Qb nqo\nendchip\n"76
}78
// 초기화 플립플롭 넷이 CLK 와 RST 를 함께 씁니다.79
function 레지스터() -> string {80
칩정의("reg4", [81
[for i in 0..<4 => 노드("d{i}", "INPUT", 0, i * 60)],82
[노드("clk", "INPUT", 0, 240), 노드("rst", "INPUT", 0, 300)],83
[for i in 0..<4 => 칩("f{i}", "dffr", 120, i * 80)],84
[for i in 0..<4 => 노드("q{i}", "OUTPUT", 320, i * 80)],85
[for i in 0..<4 for 줄 in [전선("d{i}:out", "f{i}:D"), 전선("clk:out", "f{i}:CLK"), 전선("rst:out", "f{i}:RST"), 전선("f{i}:Q", "q{i}:in0")] => 줄],86
[for i in 0..<4 => 바깥입력("D{i}", "d{i}")],87
[바깥입력("CLK", "clk"), 바깥입력("RST", "rst")],88
[for i in 0..<4 => 바깥출력("Q{i}", "q{i}")],89
])90
}92
function 올림이름(k: int) -> string {93
if k == 3 { "coutg" } else { "c{k}" }94
}96
function 앞올림(k: int) -> string {97
if k == 1 { "ia0:out" } else { "c{k - 1}:out" }98
}100
// 자리올림 입력 1 을 구조에 넣었습니다. 비트 0 은 NOT a0 이고 첫 자리올림은 a0 이라 상수 입력이 필요 없습니다.101
function 증분기() -> string {102
칩정의("inc4", [103
[for i in 0..<4 => 노드("ia{i}", "INPUT", 0, i * 60)],104
[노드("s0g", "NOT", 90, 0)],105
[for k in 1..3 for 줄 in [노드("x{k}", "XOR", 30 + k * 80, k * 60), 노드(올림이름(k), "AND", 30 + k * 80, k * 60 + 40)] => 줄],106
[for i in 0..<4 => 노드("os{i}", "OUTPUT", 360, i * 60)],107
[노드("ocout", "OUTPUT", 360, 240)],108
[전선("ia0:out", "s0g:in0"), 전선("s0g:out", "os0:in0")],109
[110
for k in 1..3 for 줄 in [111
전선("ia{k}:out", "x{k}:in0"),112
전선(앞올림(k), "x{k}:in1"),113
전선("x{k}:out", "os{k}:in0"),114
전선("ia{k}:out", "{올림이름(k)}:in0"),115
전선(앞올림(k), "{올림이름(k)}:in1"),116
] => 줄117
],118
[전선("coutg:out", "ocout:in0")],119
[for i in 0..<4 => 바깥입력("a{i}", "ia{i}")],120
[for i in 0..<4 => 바깥출력("s{i}", "os{i}")],121
[바깥출력("cout", "ocout")],122
])123
}125
// Q 에서 증분기를 거쳐 D 로 가는 고리는 레지스터에서 끊기므로 조합 부분은 비순환입니다.126
function 카운터() -> string {127
칩정의("counter4", [128
[노드("ci", "INPUT", 0, 0), 노드("ri", "INPUT", 0, 60), 칩("r", "reg4", 120, 0), 칩("i", "inc4", 360, 0)],129
[for k in 0..<4 => 노드("oq{k}", "OUTPUT", 560, k * 60)],130
[전선("ci:out", "r:CLK"), 전선("ri:out", "r:RST")],131
[for k in 0..<4 => 전선("r:Q{k}", "i:a{k}")],132
[for k in 0..<4 => 전선("i:s{k}", "r:D{k}")],133
[for k in 0..<4 => 전선("r:Q{k}", "oq{k}:in0")],134
[바깥입력("CLK", "ci"), 바깥입력("RST", "ri")],135
[for k in 0..<4 => 바깥출력("Q{k}", "oq{k}")],136
])137
}139
// 주소 두 비트를 원핫 네 선으로 풉니다.140
function 이대사디코더() -> string {141
칩정의("decoder24", [142
[노드("da0", "INPUT", 0, 0), 노드("da1", "INPUT", 0, 80), 노드("dn0", "NOT", 80, 0), 노드("dn1", "NOT", 80, 80)],143
[for k in 0..<4 => 노드("ds{k}", "AND", 170, k * 60)],144
[for k in 0..<4 => 노드("do{k}", "OUTPUT", 260, k * 60)],145
[전선("da0:out", "dn0:in0"), 전선("da1:out", "dn1:in0")],146
[for k in 0..<4 for 줄 in [전선(리터럴("da", "dn", k, 0), "ds{k}:in0"), 전선(리터럴("da", "dn", k, 1), "ds{k}:in1")] => 줄],147
[for k in 0..<4 => 전선("ds{k}:out", "do{k}:in0")],148
[바깥입력("a0", "da0"), 바깥입력("a1", "da1")],149
[for k in 0..<4 => 바깥출력("s{k}", "do{k}")],150
])151
}153
// 비트마다 LD 로 D 와 Q 를 고르는 선택기를 두어 LD 가 0 이면 지킵니다.154
function 적재레지스터() -> string {155
칩정의("reg4le", [156
[for i in 0..<4 => 노드("ed{i}", "INPUT", 0, i * 40)],157
[노드("eld", "INPUT", 0, 160), 노드("eclk", "INPUT", 0, 200), 노드("erst", "INPUT", 0, 240), 노드("enl", "NOT", 70, 160)],158
[for i in 0..<4 for 줄 in [노드("ea{i}", "AND", 130, i * 40), 노드("eb{i}", "AND", 130, i * 40 + 20), 노드("em{i}", "OR", 200, i * 40)] => 줄],159
[칩("r", "reg4", 280, 0)],160
[for i in 0..<4 => 노드("eq{i}", "OUTPUT", 460, i * 40)],161
[전선("eld:out", "enl:in0")],162
[163
for i in 0..<4 for 줄 in [164
전선("eld:out", "ea{i}:in0"),165
전선("ed{i}:out", "ea{i}:in1"),166
전선("enl:out", "eb{i}:in0"),167
전선("r:Q{i}", "eb{i}:in1"),168
전선("ea{i}:out", "em{i}:in0"),169
전선("eb{i}:out", "em{i}:in1"),170
전선("em{i}:out", "r:D{i}"),171
] => 줄172
],173
[전선("eclk:out", "r:CLK"), 전선("erst:out", "r:RST")],174
[for i in 0..<4 => 전선("r:Q{i}", "eq{i}:in0")],175
[for i in 0..<4 => 바깥입력("D{i}", "ed{i}")],176
[바깥입력("LD", "eld"), 바깥입력("CLK", "eclk"), 바깥입력("RST", "erst")],177
[for i in 0..<4 => 바깥출력("Q{i}", "eq{i}")],178
])179
}181
function 워드배선(w: int) -> Array<string> {182
이어붙이기([183
[for i in 0..<4 => 전선("md{i}:out", "w{w}:D{i}")],184
[전선("ml{w}:out", "w{w}:LD"), 전선("mclk:out", "w{w}:CLK"), 전선("mrst:out", "w{w}:RST")],185
])186
}188
// 비트 b 의 읽기는 워드마다 AND(선택, Q) 를 두고 넷을 두 단 OR 로 모읍니다.189
function 램읽기노드(b: int) -> Array<string> {190
이어붙이기([191
[for k in 0..<4 => 노드("m{b}{k}", "AND", 700, b * 150 + k * 30)],192
[노드("m{b}4", "OR", 790, b * 150), 노드("m{b}5", "OR", 790, b * 150 + 60), 노드("m{b}6", "OR", 870, b * 150)],193
])194
}196
function 램읽기배선(b: int) -> Array<string> {197
이어붙이기([198
[for k in 0..<4 for 줄 in [전선("dec:s{k}", "m{b}{k}:in0"), 전선("w{k}:Q{b}", "m{b}{k}:in1")] => 줄],199
[200
전선("m{b}0:out", "m{b}4:in0"),201
전선("m{b}1:out", "m{b}4:in1"),202
전선("m{b}2:out", "m{b}5:in0"),203
전선("m{b}3:out", "m{b}5:in1"),204
전선("m{b}4:out", "m{b}6:in0"),205
전선("m{b}5:out", "m{b}6:in1"),206
전선("m{b}6:out", "mq{b}:in0"),207
],208
])209
}211
// 4워드 4비트 레지스터 파일입니다. 고른 워드의 적재만 AND(선택, WE) 로 열고 읽기는 원핫 선택으로 워드를 고르는 조합 회로입니다.212
function 기억장치() -> string {213
칩정의("ram4", [214
[노드("ma0", "INPUT", 0, 0), 노드("ma1", "INPUT", 0, 40)],215
[for i in 0..<4 => 노드("md{i}", "INPUT", 0, 80 + i * 40)],216
[노드("mwe", "INPUT", 0, 240), 노드("mclk", "INPUT", 0, 280), 노드("mrst", "INPUT", 0, 320), 칩("dec", "decoder24", 130, 0)],217
[for w in 0..<4 => 노드("ml{w}", "AND", 280, w * 60)],218
[for w in 0..<4 => 칩("w{w}", "reg4le", 400, w * 200)],219
[for b in 0..<4 for 줄 in 램읽기노드(b) => 줄],220
[for b in 0..<4 => 노드("mq{b}", "OUTPUT", 950, b * 150)],221
[전선("ma0:out", "dec:a0"), 전선("ma1:out", "dec:a1")],222
[for w in 0..<4 for 줄 in [전선("dec:s{w}", "ml{w}:in0"), 전선("mwe:out", "ml{w}:in1")] => 줄],223
[for w in 0..<4 for 줄 in 워드배선(w) => 줄],224
[for b in 0..<4 for 줄 in 램읽기배선(b) => 줄],225
[바깥입력("a0", "ma0"), 바깥입력("a1", "ma1")],226
[for i in 0..<4 => 바깥입력("D{i}", "md{i}")],227
[바깥입력("WE", "mwe"), 바깥입력("CLK", "mclk"), 바깥입력("RST", "mrst")],228
[for b in 0..<4 => 바깥출력("q{b}", "mq{b}")],229
])230
}232
// 주소 세 비트를 원핫 여덟 선으로 풉니다. 선마다 두 단 AND 로 세 비트를 묶습니다.233
function 삼대팔디코더() -> string {234
칩정의("decoder38", [235
[for j in 0..<3 => 노드("dp{j}", "INPUT", 0, j * 60)],236
[for j in 0..<3 => 노드("dn{j}", "NOT", 60, j * 60)],237
[for k in 0..<8 for 줄 in [노드("de{k}", "AND", 130, k * 40), 노드("ds{k}", "AND", 200, k * 40)] => 줄],238
[for k in 0..<8 => 노드("do{k}", "OUTPUT", 280, k * 40)],239
[for j in 0..<3 => 전선("dp{j}:out", "dn{j}:in0")],240
[241
for k in 0..<8 for 줄 in [242
전선(리터럴("dp", "dn", k, 0), "de{k}:in0"),243
전선(리터럴("dp", "dn", k, 1), "de{k}:in1"),244
전선("de{k}:out", "ds{k}:in0"),245
전선(리터럴("dp", "dn", k, 2), "ds{k}:in1"),246
] => 줄247
],248
[for k in 0..<8 => 전선("ds{k}:out", "do{k}:in0")],249
[for j in 0..<3 => 바깥입력("p{j}", "dp{j}")],250
[for k in 0..<8 => 바깥출력("s{k}", "do{k}")],251
])252
}254
type 명령 = { 연산: int, 피연산: int }256
let 카운트다운: Array<명령> = [257
{ 연산: 1, 피연산: 3 },258
{ 연산: 3, 피연산: 0 },259
{ 연산: 2, 피연산: 0 },260
{ 연산: 5, 피연산: 1 },261
{ 연산: 3, 피연산: 0 },262
{ 연산: 7, 피연산: 7 },263
{ 연산: 6, 피연산: 2 },264
{ 연산: 0, 피연산: 0 },265
]267
function 롬주소들(비트: int) -> Array<int> {268
[for 주소 in 0..<8 if 비트인가(카운트다운[주소].연산 * 8 + 카운트다운[주소].피연산, 비트) => 주소]269
}271
// 사슬의 게이트는 앞에서부터 ra, rb, rc 이고 마지막은 ri 입니다.272
function 롬게이트(비트: int, g: int, 게이트수: int) -> string {273
let 글자 = "abcdefg".scalars()[g]274
if g == 게이트수 - 1 { "ri{비트}" } else { "r{글자}{비트}" }275
}277
function 롬노드(비트: int) -> Array<string> {278
let 게이트수 = 롬주소들(비트).length - 1279
[for g in 0..<게이트수 => 노드(롬게이트(비트, g, 게이트수), "OR", 300 + g * 80, 비트 * 40)]280
}282
// 주소가 하나뿐인 비트는 선택선을 출력에 바로 잇습니다.283
function 롬배선(비트: int) -> Array<string> {284
let 주소들 = 롬주소들(비트)285
let 게이트수 = 주소들.length - 1286
if 게이트수 == 0 { return [전선("dec:s{주소들[0]}", "io{비트}:in0")] }287
이어붙이기([288
[289
for g in 0..<게이트수 for 줄 in [290
전선(if g == 0 { "dec:s{주소들[0]}" } else { "{롬게이트(비트, g - 1, 게이트수)}:out" }, "{롬게이트(비트, g, 게이트수)}:in0"),291
전선("dec:s{주소들[g + 1]}", "{롬게이트(비트, g, 게이트수)}:in1"),292
] => 줄293
],294
[전선("{롬게이트(비트, 게이트수 - 1, 게이트수)}:out", "io{비트}:in0")],295
])296
}298
// 명령 비트마다 그 비트가 1 인 주소의 선택선을 OR 로 묶어 상수를 배선으로만 만듭니다.299
// 인코딩은 op2 op1 op0 | x2 x1 x0 이고 000 HALT, 001 LDI, 010 LOAD, 011 STORE, 100 ADD, 101 SUBI, 110 JMP, 111 JZ 입니다.300
// 프로그램은 0 LDI3, 1 STORE0, 2 LOAD0, 3 SUBI1, 4 STORE0, 5 JZ7, 6 JMP2, 7 HALT 인 카운트다운입니다.301
function 명령기억장치() -> string {302
칩정의("rom8x6_demo", [303
[for j in 0..<3 => 노드("rp{j}", "INPUT", 0, j * 40)],304
[칩("dec", "decoder38", 120, 0)],305
[for 비트 in 0..<6 for 줄 in 롬노드(비트) => 줄],306
[for 비트 in 0..<6 => 노드("io{비트}", "OUTPUT", 620, 비트 * 40)],307
[for j in 0..<3 => 전선("rp{j}:out", "dec:p{j}")],308
[for 비트 in 0..<6 for 줄 in 롬배선(비트) => 줄],309
[for j in 0..<3 => 바깥입력("p{j}", "rp{j}")],310
[for 비트 in 0..<6 => 바깥출력("i{비트}", "io{비트}")],311
])312
}314
// TAKE 가 1 이면 3비트 TARGET 을, 아니면 PC+1 을 다음 값으로 고릅니다. 상위 비트의 TARGET 은 0 입니다.315
function 프로그램카운터() -> string {316
칩정의("pc4le", [317
[for i in 0..<3 => 노드("pt{i}", "INPUT", 0, i * 40)],318
[노드("ptk", "INPUT", 0, 120), 노드("pld", "INPUT", 0, 160), 노드("pclk", "INPUT", 0, 200), 노드("prst", "INPUT", 0, 240), 노드("pnt", "NOT", 60, 120)],319
[for i in 0..<3 for 줄 in [노드("pa{i}", "AND", 200, i * 40), 노드("pb{i}", "AND", 200, i * 40 + 20), 노드("pm{i}", "OR", 280, i * 40)] => 줄],320
[노드("pa3", "AND", 200, 120), 칩("inc", "inc4", 150, 320), 칩("r", "reg4le", 400, 0)],321
[for i in 0..<4 => 노드("pq{i}", "OUTPUT", 560, i * 40)],322
[전선("ptk:out", "pnt:in0")],323
[324
for i in 0..<3 for 줄 in [325
전선("pnt:out", "pa{i}:in0"),326
전선("inc:s{i}", "pa{i}:in1"),327
전선("ptk:out", "pb{i}:in0"),328
전선("pt{i}:out", "pb{i}:in1"),329
전선("pa{i}:out", "pm{i}:in0"),330
전선("pb{i}:out", "pm{i}:in1"),331
전선("pm{i}:out", "r:D{i}"),332
] => 줄333
],334
[전선("pnt:out", "pa3:in0"), 전선("inc:s3", "pa3:in1"), 전선("pa3:out", "r:D3")],335
[for i in 0..<4 => 전선("r:Q{i}", "inc:a{i}")],336
[전선("pld:out", "r:LD"), 전선("pclk:out", "r:CLK"), 전선("prst:out", "r:RST")],337
[for i in 0..<4 => 전선("r:Q{i}", "pq{i}:in0")],338
[for i in 0..<3 => 바깥입력("TARGET{i}", "pt{i}")],339
[바깥입력("TAKE", "ptk"), 바깥입력("LD", "pld"), 바깥입력("CLK", "pclk"), 바깥입력("RST", "prst")],340
[for i in 0..<4 => 바깥출력("Q{i}", "pq{i}")],341
])342
}344
// 제어선은 모두 extin 으로 받습니다. bsrc 가 0 이면 ALU 의 B 는 램 읽기, 1 이면 즉치입니다.345
// ACC 의 D 는 accsrc1 이 1 이면 ALU 결과, 아니면 accsrc0 이 0 일 때 즉치, 1 일 때 램 읽기입니다.346
function 누산기데이터경로() -> string {347
[348
"chipdef acc_datapath4\n",349
"node cld INPUT 0 0\nnode cwe INPUT 0 30\nnode cop0 INPUT 0 60\nnode cop1 INPUT 0 90\n",350
"node cs0 INPUT 0 120\nnode cs1 INPUT 0 150\nnode cbs INPUT 0 180\n",351
"node im0 INPUT 0 210\nnode im1 INPUT 0 240\nnode im2 INPUT 0 270\nnode im3 INPUT 0 300\n",352
"node ca0 INPUT 0 330\nnode ca1 INPUT 0 360\nnode cclk INPUT 0 390\nnode crst INPUT 0 420\n",353
"chip acc reg4le 600 0\nchip ram ram4 600 220\n",354
"chip bm0 mux2 300 100\nchip bm1 mux2 300 130\nchip bm2 mux2 300 160\nchip bm3 mux2 300 190\n",355
"chip alu alu4 450 100\n",356
"chip am0 mux2 760 0\nchip am1 mux2 760 40\nchip am2 mux2 760 80\nchip am3 mux2 760 120\n",357
"chip bd0 mux2 860 0\nchip bd1 mux2 860 40\nchip bd2 mux2 860 80\nchip bd3 mux2 860 120\n",358
"node z01 OR 700 320\nnode z23 OR 700 360\nnode za OR 760 340\nnode zn NOT 820 340\n",359
"node oa0 OUTPUT 980 0\nnode oa1 OUTPUT 980 40\nnode oa2 OUTPUT 980 80\nnode oa3 OUTPUT 980 120\n",360
"node oq0 OUTPUT 980 220\nnode oq1 OUTPUT 980 250\nnode oq2 OUTPUT 980 280\nnode oq3 OUTPUT 980 310\nnode oz OUTPUT 980 360\n",361
"wire cclk:out acc:CLK\nwire crst:out acc:RST\nwire cld:out acc:LD\n",362
"wire bd0:O acc:D0\nwire bd1:O acc:D1\nwire bd2:O acc:D2\nwire bd3:O acc:D3\n",363
"wire ca0:out ram:a0\nwire ca1:out ram:a1\nwire cwe:out ram:WE\nwire cclk:out ram:CLK\nwire crst:out ram:RST\n",364
"wire acc:Q0 ram:D0\nwire acc:Q1 ram:D1\nwire acc:Q2 ram:D2\nwire acc:Q3 ram:D3\n",365
"wire ram:q0 bm0:D0\nwire im0:out bm0:D1\nwire cbs:out bm0:S\n",366
"wire ram:q1 bm1:D0\nwire im1:out bm1:D1\nwire cbs:out bm1:S\n",367
"wire ram:q2 bm2:D0\nwire im2:out bm2:D1\nwire cbs:out bm2:S\n",368
"wire ram:q3 bm3:D0\nwire im3:out bm3:D1\nwire cbs:out bm3:S\n",369
"wire acc:Q0 alu:a0\nwire acc:Q1 alu:a1\nwire acc:Q2 alu:a2\nwire acc:Q3 alu:a3\n",370
"wire bm0:O alu:b0\nwire bm1:O alu:b1\nwire bm2:O alu:b2\nwire bm3:O alu:b3\n",371
"wire cop0:out alu:op0\nwire cop1:out alu:op1\n",372
"wire im0:out am0:D0\nwire ram:q0 am0:D1\nwire cs0:out am0:S\n",373
"wire im1:out am1:D0\nwire ram:q1 am1:D1\nwire cs0:out am1:S\n",374
"wire im2:out am2:D0\nwire ram:q2 am2:D1\nwire cs0:out am2:S\n",375
"wire im3:out am3:D0\nwire ram:q3 am3:D1\nwire cs0:out am3:S\n",376
"wire am0:O bd0:D0\nwire alu:y0 bd0:D1\nwire cs1:out bd0:S\n",377
"wire am1:O bd1:D0\nwire alu:y1 bd1:D1\nwire cs1:out bd1:S\n",378
"wire am2:O bd2:D0\nwire alu:y2 bd2:D1\nwire cs1:out bd2:S\n",379
"wire am3:O bd3:D0\nwire alu:y3 bd3:D1\nwire cs1:out bd3:S\n",380
"wire acc:Q0 z01:in0\nwire acc:Q1 z01:in1\nwire acc:Q2 z23:in0\nwire acc:Q3 z23:in1\n",381
"wire z01:out za:in0\nwire z23:out za:in1\nwire za:out zn:in0\n",382
"wire acc:Q0 oa0:in0\nwire acc:Q1 oa1:in0\nwire acc:Q2 oa2:in0\nwire acc:Q3 oa3:in0\n",383
"wire ram:q0 oq0:in0\nwire ram:q1 oq1:in0\nwire ram:q2 oq2:in0\nwire ram:q3 oq3:in0\nwire zn:out oz:in0\n",384
"extin acc_ld cld\nextin ram_we cwe\nextin alu_op0 cop0\nextin alu_op1 cop1\n",385
"extin accsrc0 cs0\nextin accsrc1 cs1\nextin bsrc cbs\n",386
"extin imm0 im0\nextin imm1 im1\nextin imm2 im2\nextin imm3 im3\nextin a0 ca0\nextin a1 ca1\n",387
"extin CLK cclk\nextin RST crst\n",388
"extout ACC0 oa0\nextout ACC1 oa1\nextout ACC2 oa2\nextout ACC3 oa3\n",389
"extout q0 oq0\nextout q1 oq1\nextout q2 oq2\nextout q3 oq3\nextout acc_zero oz\n",390
"endchip\n",391
].join("")392
}394
// opcode 를 원핫으로 풀어 제어식을 게이트로 만듭니다. 이 처리장치는 ADD 와 SUB 만 쓰므로 alu_op1 은 늘 0 입니다.395
function 제어장치() -> string {396
"chipdef cpu_control\nnode cop0 INPUT 0 0\nnode cop1 INPUT 0 40\nnode cop2 INPUT 0 80\nnode caz INPUT 0 120\nchip dec decoder38 80 0\nnode cor1 OR 250 0\nnode cor2 OR 250 40\nnode cald OR 330 20\nnode cnop0 NOT 250 100\nnode cz0 AND 330 100\nnode cjz AND 250 160\nnode ctpc OR 330 160\nnode cnh NOT 250 220\nnode oald OUTPUT 430 0\nnode owe OUTPUT 430 40\nnode oa0 OUTPUT 430 80\nnode oa1 OUTPUT 430 120\nnode obs OUTPUT 430 160\nnode oas1 OUTPUT 430 200\nnode oas0 OUTPUT 430 240\nnode otp OUTPUT 430 280\nnode opl OUTPUT 430 320\nwire cop0:out dec:p0\nwire cop1:out dec:p1\nwire cop2:out dec:p2\nwire dec:s1 cor1:in0\nwire dec:s2 cor1:in1\nwire dec:s4 cor2:in0\nwire dec:s5 cor2:in1\nwire cor1:out cald:in0\nwire cor2:out cald:in1\nwire cald:out oald:in0\nwire dec:s3 owe:in0\nwire dec:s5 oa0:in0\nwire dec:s5 obs:in0\nwire cop0:out cnop0:in0\nwire cop0:out cz0:in0\nwire cnop0:out cz0:in1\nwire cz0:out oa1:in0\nwire cor2:out oas1:in0\nwire dec:s2 oas0:in0\nwire dec:s7 cjz:in0\nwire caz:out cjz:in1\nwire dec:s6 ctpc:in0\nwire cjz:out ctpc:in1\nwire ctpc:out otp:in0\nwire dec:s0 cnh:in0\nwire cnh:out opl:in0\nextin op0 cop0\nextin op1 cop1\nextin op2 cop2\nextin acc_zero caz\nextout acc_ld oald\nextout ram_we owe\nextout alu_op0 oa0\nextout alu_op1 oa1\nextout bsrc obs\nextout accsrc0 oas0\nextout accsrc1 oas1\nextout take_pc otp\nextout pc_ld opl\nendchip\n"397
}399
type 세그먼트 = { 이름: string, 숫자들: Array<int>, 높이들: Array<int> }400
type 오어게이트 = { 아이디: string, 입력0: string, 입력1: string, 층: int }402
// 높이들은 OR 게이트를 만드는 순서대로 놓을 세로 위치이고 그림을 보기 좋게 맞춘 값입니다.403
let 세그먼트표: Array<세그먼트> = [404
{ 이름: "a", 숫자들: [0, 2, 3, 5, 6, 7, 8, 9], 높이들: [0, 30, 60, 90, 15, 75, 45] },405
{ 이름: "b", 숫자들: [0, 1, 2, 3, 4, 7, 8, 9], 높이들: [130, 160, 190, 220, 145, 205, 175] },406
{ 이름: "c", 숫자들: [0, 1, 3, 4, 5, 6, 7, 8, 9], 높이들: [260, 290, 320, 350, 275, 335, 305, 320] },407
{ 이름: "d", 숫자들: [0, 2, 3, 5, 6, 8, 9], 높이들: [390, 420, 450, 405, 460, 430] },408
{ 이름: "e", 숫자들: [0, 2, 6, 8], 높이들: [500, 530, 515] },409
{ 이름: "f", 숫자들: [0, 4, 5, 6, 8, 9], 높이들: [570, 600, 630, 585, 605] },410
{ 이름: "g", 숫자들: [2, 3, 4, 5, 6, 8, 9], 높이들: [670, 700, 730, 685, 745, 715] },411
]413
// 켜지는 숫자의 선택선을 왼쪽부터 둘씩 OR 로 묶고 홀로 남은 항목은 다음 층 끝으로 넘깁니다. 첫 층은 l, 그 위 층은 m 으로 번호를 이어 매기고 마지막 하나는 o 입니다.414
function 오어나무(세그: 세그먼트) -> Array<오어게이트> {415
let mut 게이트들: Array<오어게이트> = []416
let mut 항목들 = 세그.숫자들.map(숫자 => "s{숫자}")417
let mut 층 = 1418
let mut 중간수 = 0419
while 항목들.length > 1 {420
let mut 다음항목들: Array<string> = []421
for k in 0..<(항목들.length / 2) {422
let 아이디 = if 항목들.length == 2 { "o{세그.이름}" } else if 층 == 1 { "{세그.이름}l{k + 1}" } else { "{세그.이름}m{중간수 + 1}" }423
if 층 > 1 { 중간수 += 1 }424
게이트들.push({ 아이디: 아이디, 입력0: 항목들[2 * k], 입력1: 항목들[2 * k + 1], 층: 층 })425
다음항목들.push(아이디)426
}427
if 항목들.length % 2 == 1 { 다음항목들.push(항목들[항목들.length - 1]) }428
항목들 = 다음항목들429
층 += 1430
}431
게이트들432
}434
function 세그먼트노드(세그: 세그먼트) -> Array<string> {435
let 게이트들 = 오어나무(세그)436
let 끝 = 게이트들.length - 1437
이어붙이기([438
[for i in 0..<게이트들.length => 노드(게이트들[i].아이디, "OR", 220 + 게이트들[i].층 * 80, 세그.높이들[i])],439
[노드("ou{세그.이름}", "OUTPUT", 300 + 게이트들[끝].층 * 80, 세그.높이들[끝])],440
])441
}443
function 세그먼트배선(세그: 세그먼트) -> Array<string> {444
이어붙이기([445
[for 게이트 in 오어나무(세그) for 줄 in [전선("{게이트.입력0}:out", "{게이트.아이디}:in0"), 전선("{게이트.입력1}:out", "{게이트.아이디}:in1")] => 줄],446
[전선("o{세그.이름}:out", "ou{세그.이름}:in0")],447
])448
}450
// 숫자 0..9 를 원핫으로 풀고 세그먼트마다 켜지는 숫자를 OR 로 묶습니다. 10 부터 15 는 모든 선이 꺼집니다.451
function 세그먼트디코더() -> string {452
칩정의("seg7", [453
[for j in 0..<4 => 노드("b{j}", "INPUT", 0, j * 40)],454
[for j in 0..<4 => 노드("nb{j}", "NOT", 60, j * 40)],455
[for d in 0..<10 for 줄 in [노드("pa{d}", "AND", 140, d * 60), 노드("pb{d}", "AND", 140, d * 60 + 30), 노드("s{d}", "AND", 220, d * 60 + 15)] => 줄],456
[for 세그 in 세그먼트표 for 줄 in 세그먼트노드(세그) => 줄],457
[for j in 0..<4 => 전선("b{j}:out", "nb{j}:in0")],458
[459
for d in 0..<10 for 줄 in [460
전선(리터럴("b", "nb", d, 0), "pa{d}:in0"),461
전선(리터럴("b", "nb", d, 1), "pa{d}:in1"),462
전선(리터럴("b", "nb", d, 2), "pb{d}:in0"),463
전선(리터럴("b", "nb", d, 3), "pb{d}:in1"),464
전선("pa{d}:out", "s{d}:in0"),465
전선("pb{d}:out", "s{d}:in1"),466
] => 줄467
],468
[for 세그 in 세그먼트표 for 줄 in 세그먼트배선(세그) => 줄],469
[for j in 0..<4 => 바깥입력("b{j}", "b{j}")],470
[for 세그 in 세그먼트표 => 바깥출력(세그.이름, "ou{세그.이름}")],471
])472
}474
// 의존하는 칩이 먼저 오도록 정의 순서를 지킵니다.475
export function 순차라이브러리() -> string {476
셋리셋래치() + 데이터래치() + 데이터플립플롭() + 초기화셋리셋래치() + 초기화데이터래치() + 초기화플립플롭() + 레지스터() + 증분기() + 카운터() + 이대사디코더() + 적재레지스터() + 기억장치() + 삼대팔디코더() + 명령기억장치() + 프로그램카운터() + 세그먼트디코더()477
}479
// CLK 와 RST 만 받아 롬의 프로그램을 실행합니다. imm3 은 XOR(clk, clk) 로 만든 구조적 0 입니다.480
function 작은처리장치() -> string {481
"chipdef tinycpu\nnode clk INPUT 20 20\nnode rst INPUT 20 80\nnode zc XOR 20 160\nchip rom rom8x6_demo 140 20\nchip pc pc4le 140 320\nchip cu cpu_control 380 20\nchip dp acc_datapath4 380 320\nnode po0 OUTPUT 680 20\nnode po1 OUTPUT 680 60\nnode po2 OUTPUT 680 100\nnode po3 OUTPUT 680 140\nnode ao0 OUTPUT 680 200\nnode ao1 OUTPUT 680 240\nnode ao2 OUTPUT 680 280\nnode ao3 OUTPUT 680 320\nnode mo0 OUTPUT 680 380\nnode mo1 OUTPUT 680 420\nnode mo2 OUTPUT 680 460\nnode mo3 OUTPUT 680 500\nwire pc:Q0 rom:p0\nwire pc:Q1 rom:p1\nwire pc:Q2 rom:p2\nwire rom:i3 cu:op0\nwire rom:i4 cu:op1\nwire rom:i5 cu:op2\nwire rom:i0 dp:imm0\nwire rom:i0 dp:a0\nwire rom:i0 pc:TARGET0\nwire rom:i1 dp:imm1\nwire rom:i1 dp:a1\nwire rom:i1 pc:TARGET1\nwire rom:i2 dp:imm2\nwire rom:i2 pc:TARGET2\nwire clk:out zc:in0\nwire clk:out zc:in1\nwire zc:out dp:imm3\nwire dp:acc_zero cu:acc_zero\nwire cu:acc_ld dp:acc_ld\nwire cu:ram_we dp:ram_we\nwire cu:alu_op0 dp:alu_op0\nwire cu:alu_op1 dp:alu_op1\nwire cu:bsrc dp:bsrc\nwire cu:accsrc0 dp:accsrc0\nwire cu:accsrc1 dp:accsrc1\nwire cu:take_pc pc:TAKE\nwire cu:pc_ld pc:LD\nwire clk:out pc:CLK\nwire clk:out dp:CLK\nwire rst:out pc:RST\nwire rst:out dp:RST\nwire pc:Q0 po0:in0\nwire pc:Q1 po1:in0\nwire pc:Q2 po2:in0\nwire pc:Q3 po3:in0\nwire dp:ACC0 ao0:in0\nwire dp:ACC1 ao1:in0\nwire dp:ACC2 ao2:in0\nwire dp:ACC3 ao3:in0\nwire dp:q0 mo0:in0\nwire dp:q1 mo1:in0\nwire dp:q2 mo2:in0\nwire dp:q3 mo3:in0\nextin CLK clk\nextin RST rst\nextout PC0 po0\nextout PC1 po1\nextout PC2 po2\nextout PC3 po3\nextout ACC0 ao0\nextout ACC1 ao1\nextout ACC2 ao2\nextout ACC3 ao3\nextout M0 mo0\nextout M1 mo1\nextout M2 mo2\nextout M3 mo3\nendchip\n"482
}484
// alu 와 seq 라이브러리의 칩을 쓰므로 main 이 그 둘 뒤에 붙입니다.485
export function 처리장치라이브러리() -> string {486
누산기데이터경로() + 제어장치() + 작은처리장치()487
}SHA256 622a12a68ac21579a388ff27317ced47dcc3809fcf7ea2cc5c220f6493e4a61b