STUDIO HAZE · jeniapp
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20.2 · 63 files
apps/circuit/core/hierarchy.tpz
Topaz · 23336 B · Original file
1
// 칩 계층. chipdef 블록을 칩 정의로, chip 줄을 인스턴스로 읽고, 평가할 때는 칩 경계를 지워 원시 게이트 회로로 펼칩니다.2
// 입력은 node, wire, chip, chipdef 이름 ... endchip, 칩 안의 extin 핀 노드와 extout 핀 노드 줄이고 출력은 분해 결과나 첫 오류입니다.3
// 같은 입력이면 늘 같은 분해와 같은 평탄 회로가 나옵니다. 칩의 진리는 펼친 회로의 진리와 같습니다.4
// 그리거나 직렬화하지 않습니다. 칩은 앞에서 정의한 칩만 쓸 수 있어 정의 순환을 받지 않습니다.5
// Chip hierarchy: parse chip definitions and instances, flatten them to primitive gates and evaluate nested views.7
import core.netlist8
import core.sim9
import core.fault10
import core.util12
export type 외부핀 = { 핀: string, 노드: string }13
export type 인스턴스 = { 아이디: string, 칩이름: string, x: int, y: int }14
export type 끝점 = { 노드: string, 핀: string }15
// 표들은 전선과 핀을 찾을 때마다 배열을 훑지 않도록 한 번 만든 색인입니다. 같은 키가 여럿이면 배열의 마지막 것이 남습니다.16
// 구동표는 끝 노드:끝 핀에서 그 핀을 구동하는 시작 끝점으로 갑니다. 전선 끝점에는 : 가 없어 키가 겹치지 않습니다.17
export type 회로본체 = { 노드들: Array<netlist.노드>, 전선들: Array<netlist.전선>, 인스턴스들: Array<인스턴스>, 구동표: Map<string, 끝점> }18
// 본체는 펼치기 전 구조이고 내부는 펼친 원시 회로입니다. 내보내기와 칩 안 보기는 본체를 씁니다.19
// 외부입력표는 내부 노드에서 extin 핀으로, 외부출력표는 extout 핀에서 내부 노드로, 터미널구동표는 내부 끝 노드에서 그 노드를 구동하는 끝점으로 갑니다.20
export type 칩정의 = {21
이름: string,22
본체: 회로본체,23
내부: netlist.회로,24
외부입력: Array<외부핀>,25
외부출력: Array<외부핀>,26
외부입력표: Map<string, string>,27
외부출력표: Map<string, string>,28
출력터미널들: Set<string>,29
터미널구동표: Map<string, 끝점>,30
}31
export type 분해결과 = { 정의들: Array<칩정의>, 정의표: Map<string, 칩정의>, 본체: 회로본체 }32
export type 놓인칩 = { 배치: 인스턴스, 정의: 칩정의 }33
export type 보기단계 = { 분해: 분해결과, 시뮬: sim.시뮬결과, 고장들: Array<fault.고장> }34
export type 경로보기 = { 단계: 보기단계, 이름들: Array<string> }36
type 줄머리 = "chipdef" | "endchip" | "node" | "wire" | "chip" | "extin" | "extout" | "기타"37
type 핀쪽 = "입력" | "출력"38
enum 본체항목 { 노드줄(Array<string>), 전선줄(netlist.전선), 칩줄(인스턴스), 외부입력줄(외부핀), 외부출력줄(외부핀) }39
// node 줄은 앞에서 정의한 칩이 모두 모인 뒤 netlist 가 검증하므로 토큰으로 둡니다.40
type 생본체 = {41
노드줄들: Array<Array<string>>,42
전선들: Array<netlist.전선>,43
인스턴스들: Array<인스턴스>,44
외부입력: Array<외부핀>,45
외부출력: Array<외부핀>,46
}47
type 생칩정의 = { 이름: string, 본체: 생본체 }49
let 빈끝점: 끝점 = { 노드: "", 핀: "" }50
let 빈정의: 칩정의 = {51
이름: "",52
본체: { 노드들: [], 전선들: [], 인스턴스들: [], 구동표: Map.new() },53
내부: { 노드들: [], 전선들: [] },54
외부입력: [],55
외부출력: [],56
외부입력표: Map.new(),57
외부출력표: Map.new(),58
출력터미널들: Set.of(),59
터미널구동표: Map.new(),60
}62
function 이름공간(칩아이디: string, 노드아이디: string) -> string { 칩아이디 + "__" + 노드아이디 }64
function 구동표만들기(전선들: Array<netlist.전선>) -> Map<string, 끝점> {65
let mut 표: Map<string, 끝점> = Map.new()66
for 선 in 전선들 { 표.insert(선.끝노드 + ":" + 선.끝핀, { 노드: 선.시작노드, 핀: 선.시작핀 }) }67
표68
}70
// extout 터미널은 OUTPUT 이라 in0 하나만 받으므로 끝 노드만 키로 씁니다.71
function 터미널구동표만들기(전선들: Array<netlist.전선>) -> Map<string, 끝점> {72
let mut 표: Map<string, 끝점> = Map.new()73
for 선 in 전선들 { 표.insert(선.끝노드, { 노드: 선.시작노드, 핀: 선.시작핀 }) }74
표75
}77
function 노드에서핀표(핀들: Array<외부핀>) -> Map<string, string> {78
let mut 표: Map<string, string> = Map.new()79
for 핀값 in 핀들 { 표.insert(핀값.노드, 핀값.핀) }80
표81
}83
function 핀에서노드표(핀들: Array<외부핀>) -> Map<string, string> {84
let mut 표: Map<string, string> = Map.new()85
for 핀값 in 핀들 { 표.insert(핀값.핀, 핀값.노드) }86
표87
}89
function 정의표만들기(정의들: Array<칩정의>) -> Map<string, 칩정의> {90
let mut 표: Map<string, 칩정의> = Map.new()91
for 정의 in 정의들 { 표.insert(정의.이름, 정의) }92
표93
}95
function 분해결과만들기(정의들: Array<칩정의>, 본체값: 회로본체) -> 분해결과 {96
{ 정의들: 정의들, 정의표: 정의표만들기(정의들), 본체: 본체값 }97
}99
function 정의찾기(정의표: Map<string, 칩정의>, 이름: string) -> 칩정의 {100
정의표.getOr(이름, 빈정의)101
}103
function 놓인칩으로(정의표: Map<string, 칩정의>, 배치: 인스턴스) -> 놓인칩 {104
{ 배치: 배치, 정의: 정의찾기(정의표, 배치.칩이름) }105
}107
// 같은 아이디의 인스턴스가 여럿이면 마지막 것을 씁니다.108
function 칩찾기(정의표: Map<string, 칩정의>, 인스턴스들: Array<인스턴스>, 아이디: string) -> Option<놓인칩> {109
let 맞는배치들 = 인스턴스들.filter(배치 => 배치.아이디 == 아이디)110
if 맞는배치들.length == 0 { None } else { Some(놓인칩으로(정의표, 맞는배치들[맞는배치들.length - 1])) }111
}113
export function 놓인칩찾기(분해값: 분해결과, 아이디: string) -> Option<놓인칩> {114
칩찾기(분해값.정의표, 분해값.본체.인스턴스들, 아이디)115
}117
export function 놓인칩들(분해값: 분해결과) -> Array<놓인칩> {118
분해값.본체.인스턴스들.map(배치 => 놓인칩으로(분해값.정의표, 배치))119
}121
// 한 내부 노드에 외부 핀이 여럿 붙으면 마지막 핀을 씁니다.122
function 외부입력핀(정의: 칩정의, 노드아이디: string) -> string {123
정의.외부입력표.getOr(노드아이디, "")124
}126
function 출력터미널인가(정의: 칩정의, 노드아이디: string) -> bool {127
노드아이디 in 정의.출력터미널들128
}130
// 같은 노드에 외부 출력 핀이 여럿 붙어도 존재만 묻습니다. 칩 정의를 만들 때 한 번 모읍니다.131
function 출력터미널모으기(핀들: Array<외부핀>) -> Set<string> {132
let mut 노드들: Set<string> = Set.of()133
for 핀값 in 핀들 { 노드들.add(핀값.노드) }134
노드들135
}137
function 터미널인가(정의: 칩정의, 노드아이디: string) -> bool {138
외부입력핀(정의, 노드아이디) != "" || 출력터미널인가(정의, 노드아이디)139
}141
function 구동자(본체값: 회로본체, 노드: string, 핀: string) -> 끝점 {142
본체값.구동표.getOr(노드 + ":" + 핀, 빈끝점)143
}145
function 시작바꾸기(선: netlist.전선, 원천: 끝점) -> netlist.전선 {146
선{ 시작노드: 원천.노드, 시작핀: 원천.핀 }147
}149
// 칩 출력 핀을 칩 안에서 그 핀을 구동하는 원시 노드로 바꿉니다. 칩 정의는 이미 평탄하고 인스턴스 아이디에는 __ 가 없어 한 번만 내려가면 됩니다.150
export function 평탄원천(분해값: 분해결과, 노드: string, 핀: string) -> 끝점 {151
match 놓인칩찾기(분해값, 노드) {152
case None => ({ 노드: 노드, 핀: 핀 })153
case Some(칩) => {154
let 터미널 = 칩.정의.외부출력표.getOr(핀, "")155
let 원천 = 터미널구동자(칩.정의, 터미널)156
if 터미널 == "" || 원천.노드 == "" { 빈끝점 } else { { 노드: 이름공간(노드, 원천.노드), 핀: 원천.핀 } }157
}158
}159
}161
function 터미널구동자(정의: 칩정의, 노드: string) -> 끝점 {162
정의.터미널구동표.getOr(노드, 빈끝점)163
}165
// 칩 입력 핀의 신호는 바깥에서 그 핀을 구동하는 원시 노드가 들고 있습니다.166
export function 칩입력원천(분해값: 분해결과, 칩아이디: string, 핀: string) -> 끝점 {167
let 구동 = 구동자(분해값.본체, 칩아이디, 핀)168
평탄원천(분해값, 구동.노드, 구동.핀)169
}171
// extin 터미널에서 나가는 칩 안 전선은 바깥 구동자에 바로 잇고, extout 터미널로 가는 전선은 바깥쪽 전선이 대신합니다.172
function 칩안전선(분해값: 분해결과, 칩: 놓인칩, 선: netlist.전선) -> netlist.전선 {173
let 입력핀 = 외부입력핀(칩.정의, 선.시작노드)174
let 원천 = if 입력핀 == "" { { 노드: 이름공간(칩.배치.아이디, 선.시작노드), 핀: 선.시작핀 } } else { 칩입력원천(분해값, 칩.배치.아이디, 입력핀) }175
{ 시작노드: 원천.노드, 시작핀: 원천.핀, 끝노드: 이름공간(칩.배치.아이디, 선.끝노드), 끝핀: 선.끝핀 }176
}178
// 본체 노드, 칩마다 터미널을 뺀 내부 노드, 원시 사이 전선, 칩 안 전선, 칩 출력에서 나가는 전선 순서입니다.179
// 이 순서가 신호 벡터와 정착 상태 문자열의 순서이므로 바꾸지 않습니다.180
export function 평탄화(분해값: 분해결과) -> netlist.회로 {181
let 본체값 = 분해값.본체182
let 칩아이디들 = 본체값.인스턴스들.map(배치 => 배치.아이디)183
let 칩들 = 놓인칩들(분해값)184
let 칩노드들 = [for 칩 in 칩들 for 노드값 in 칩.정의.내부.노드들 if !터미널인가(칩.정의, 노드값.아이디) => 노드값{185
아이디: 이름공간(칩.배치.아이디, 노드값.아이디),186
x: 칩.배치.x + 노드값.x,187
y: 칩.배치.y + 노드값.y,188
}]189
let 원시전선들 = 본체값.전선들.filter(선 => !(선.시작노드 in 칩아이디들) && !(선.끝노드 in 칩아이디들))190
let 칩안전선들 = [for 칩 in 칩들 for 선 in 칩.정의.내부.전선들 if !출력터미널인가(칩.정의, 선.끝노드) => 칩안전선(분해값, 칩, 선)]191
let 칩출력전선들 = [for 선 in 본체값.전선들 if 선.시작노드 in 칩아이디들 && !(선.끝노드 in 칩아이디들) => 시작바꾸기(선, 평탄원천(분해값, 선.시작노드, 선.시작핀))]192
{193
노드들: [for 묶음 in [본체값.노드들, 칩노드들] for 노드값 in 묶음 => 노드값],194
전선들: [for 묶음 in [원시전선들, 칩안전선들, 칩출력전선들] for 선 in 묶음 if 선.시작노드 != "" => 선],195
}196
}198
function 줄머리로(토큰: string) -> 줄머리 {199
match 토큰 {200
case "chipdef" => "chipdef"201
case "endchip" => "endchip"202
case "node" => "node"203
case "wire" => "wire"204
case "chip" => "chip"205
case "extin" => "extin"206
case "extout" => "extout"207
case _ => "기타"208
}209
}211
function 알수없는줄(칩안: bool, 머리: string) -> string {212
if 칩안 { "칩 정의 안의 알 수 없는 줄입니다: " + 머리 } else { "알 수 없는 줄입니다: " + 머리 }213
}215
function 칩머리읽기(토큰들: Array<string>) -> Result<string, string> {216
if 토큰들.length != 2 { return Err("chipdef 줄 형식은 chipdef 이름 입니다") }217
if !util.안전아이디인가(토큰들[1]) { return Err("유효하지 않은 칩 이름입니다: " + 토큰들[1]) }218
Ok(토큰들[1])219
}221
function 인스턴스줄파싱(토큰들: Array<string>) -> Result<인스턴스, string> {222
if 토큰들.length != 5 { return Err("chip 줄 형식은 chip 인스턴스 칩이름 x y 입니다") }223
if !util.안전아이디인가(토큰들[1]) { return Err("유효하지 않은 인스턴스 아이디입니다: " + 토큰들[1]) }224
if !util.안전아이디인가(토큰들[2]) { return Err("유효하지 않은 칩 이름입니다: " + 토큰들[2]) }225
if !util.정수인가(토큰들[3]) || !util.정수인가(토큰들[4]) { return Err("칩 좌표는 정수여야 합니다") }226
Ok({ 아이디: 토큰들[1], 칩이름: 토큰들[2], x: toInt(토큰들[3]) ?? 0, y: toInt(토큰들[4]) ?? 0 })227
}229
// 토큰이 셋이 아닌 extin 과 extout 은 알 수 없는 줄로 봅니다.230
function 외부핀줄읽기(칩안: bool, 머리: 줄머리, 토큰들: Array<string>) -> Result<본체항목, string> {231
if !칩안 || 토큰들.length != 3 { return Err(알수없는줄(칩안, 토큰들[0])) }232
if !util.안전아이디인가(토큰들[1]) { return Err("유효하지 않은 외부 핀 이름입니다: " + 토큰들[1]) }233
let 핀값 = { 핀: 토큰들[1], 노드: 토큰들[2] }234
Ok(if 머리 == "extin" { 본체항목.외부입력줄(핀값) } else { 본체항목.외부출력줄(핀값) })235
}237
function 본체줄읽기(칩안: bool, 머리: 줄머리, 토큰들: Array<string>) -> Result<본체항목, string> {238
match 머리 {239
case "node" => Ok(본체항목.노드줄(토큰들))240
case "wire" => Ok(본체항목.전선줄(netlist.전선줄파싱(토큰들)?))241
case "chip" => Ok(본체항목.칩줄(인스턴스줄파싱(토큰들)?))242
case "extin" | "extout" => 외부핀줄읽기(칩안, 머리, 토큰들)243
case "chipdef" | "endchip" | "기타" => Err(알수없는줄(칩안, 토큰들[0]))244
}245
}247
function 본체모으기(항목들: Array<본체항목>) -> 생본체 {248
let mut 노드줄들: Array<Array<string>> = []249
let mut 전선들: Array<netlist.전선> = []250
let mut 인스턴스들: Array<인스턴스> = []251
let mut 외부입력: Array<외부핀> = []252
let mut 외부출력: Array<외부핀> = []253
for 항목 in 항목들 {254
match 항목 {255
case 노드줄(토큰들) => { 노드줄들.push(토큰들) }256
case 전선줄(선) => { 전선들.push(선) }257
case 칩줄(배치) => { 인스턴스들.push(배치) }258
case 외부입력줄(핀값) => { 외부입력.push(핀값) }259
case 외부출력줄(핀값) => { 외부출력.push(핀값) }260
}261
}262
{ 노드줄들: 노드줄들, 전선들: 전선들, 인스턴스들: 인스턴스들, 외부입력: 외부입력, 외부출력: 외부출력 }263
}265
function 칩핀없음인가(정의표: Map<string, 칩정의>, 인스턴스들: Array<인스턴스>, 노드: string, 핀: string, 쪽: 핀쪽) -> bool {266
match 칩찾기(정의표, 인스턴스들, 노드) {267
case None => false268
case Some(칩) => {269
let 핀들 = match 쪽 {270
case "입력" => 칩.정의.외부입력271
case "출력" => 칩.정의.외부출력272
}273
핀들.filter(핀값 => 핀값.핀 == 핀).length == 0274
}275
}276
}278
// 원시 노드끼리의 전선은 netlist 가 검증하고, 칩에 닿는 전선은 그 칩이 선언한 extin 이나 extout 핀이어야 합니다.279
function 본체검증(정의들: Array<칩정의>, 생본체값: 생본체) -> Result<회로본체, string> {280
let 칩아이디들 = 생본체값.인스턴스들.map(배치 => 배치.아이디)281
let 원시전선들 = 생본체값.전선들.filter(선 => !(선.시작노드 in 칩아이디들) && !(선.끝노드 in 칩아이디들))282
let 원시회로 = netlist.회로만들기(생본체값.노드줄들, 원시전선들)?283
let 없는칩들 = 생본체값.인스턴스들.filter(배치 => 정의들.filter(정의 => 정의.이름 == 배치.칩이름).length == 0)284
if 없는칩들.length > 0 { return Err("정의되지 않은 칩입니다: " + 없는칩들[0].칩이름) }285
let 정의표 = 정의표만들기(정의들)286
for 선 in 생본체값.전선들 {287
if 칩핀없음인가(정의표, 생본체값.인스턴스들, 선.시작노드, 선.시작핀, "출력") { return Err("없는 외부 핀입니다: " + 선.시작노드 + ":" + 선.시작핀) }288
if 칩핀없음인가(정의표, 생본체값.인스턴스들, 선.끝노드, 선.끝핀, "입력") { return Err("없는 외부 핀입니다: " + 선.끝노드 + ":" + 선.끝핀) }289
}290
Ok({ 노드들: 원시회로.노드들, 전선들: 생본체값.전선들, 인스턴스들: 생본체값.인스턴스들, 구동표: 구동표만들기(생본체값.전선들) })291
}293
function 정의만들기(앞정의들: Array<칩정의>, 생정의: 생칩정의) -> Result<칩정의, string> {294
let 본체값 = match 본체검증(앞정의들, 생정의.본체) {295
case Ok(값) => 값296
case Err(오류) => return Err("칩 " + 생정의.이름 + ": " + 오류)297
}298
let 아이디들 = 본체값.노드들.map(노드값 => 노드값.아이디)299
let 빠진입력들 = 생정의.본체.외부입력.filter(핀값 => !(핀값.노드 in 아이디들))300
if 빠진입력들.length > 0 { return Err("칩 " + 생정의.이름 + "의 extin " + 빠진입력들[0].핀 + "이 없는 내부 노드를 가리킵니다: " + 빠진입력들[0].노드) }301
let 빠진출력들 = 생정의.본체.외부출력.filter(핀값 => !(핀값.노드 in 아이디들))302
if 빠진출력들.length > 0 { return Err("칩 " + 생정의.이름 + "의 extout " + 빠진출력들[0].핀 + "이 없는 내부 노드를 가리킵니다: " + 빠진출력들[0].노드) }303
if 앞정의들.filter(정의 => 정의.이름 == 생정의.이름).length > 0 { return Err("칩 정의가 중복됩니다: " + 생정의.이름) }304
let 내부 = 평탄화(분해결과만들기(앞정의들, 본체값))305
Ok({306
이름: 생정의.이름,307
본체: 본체값,308
내부: 내부,309
외부입력: 생정의.본체.외부입력,310
외부출력: 생정의.본체.외부출력,311
외부입력표: 노드에서핀표(생정의.본체.외부입력),312
외부출력표: 핀에서노드표(생정의.본체.외부출력),313
출력터미널들: 출력터미널모으기(생정의.본체.외부출력),314
터미널구동표: 터미널구동표만들기(내부.전선들),315
})316
}318
// 인스턴스 이름공간이 부모 노드와 부딪히는 경우를 펼친 뒤에 잡습니다.319
function 충돌검사(분해값: 분해결과) -> Result<분해결과, string> {320
let mut 본아이디들: Set<string> = Set.of()321
for 노드값 in 평탄화(분해값).노드들 {322
if 노드값.아이디 in 본아이디들 { return Err("노드 아이디가 충돌합니다: " + 노드값.아이디) }323
본아이디들.add(노드값.아이디)324
}325
Ok(분해값)326
}328
// 줄 오류를 먼저 모두 보고, 그다음 칩 정의를 정의 순서로, 마지막으로 최상위 본체를 검증합니다. 첫 오류에서 멈춥니다.329
export function 분해(원문: string) -> Result<분해결과, string> {330
let mut 생정의들: Array<생칩정의> = []331
let mut 최상위항목들: Array<본체항목> = []332
let mut 칩항목들: Array<본체항목> = []333
let mut 칩이름 = ""334
let mut 칩안 = false335
for 토큰들 in 원문.split("\n").map(util.토큰나누기).filter(줄토큰들 => 줄토큰들.length > 0) {336
let 머리 = 줄머리로(토큰들[0])337
if 칩안 && 머리 == "endchip" {338
생정의들.push({ 이름: 칩이름, 본체: 본체모으기(칩항목들) })339
칩안 = false340
} else if !칩안 && 머리 == "chipdef" {341
칩이름 = 칩머리읽기(토큰들)?342
칩항목들 = []343
칩안 = true344
} else if 칩안 {345
칩항목들.push(본체줄읽기(true, 머리, 토큰들)?)346
} else {347
최상위항목들.push(본체줄읽기(false, 머리, 토큰들)?)348
}349
}350
if 칩안 { return Err("endchip가 없습니다") }351
let mut 정의들: Array<칩정의> = []352
for 생정의 in 생정의들 { 정의들.push(정의만들기(정의들, 생정의)?) }353
let 본체값 = 본체검증(정의들, 본체모으기(최상위항목들))?354
충돌검사(분해결과만들기(정의들, 본체값))355
}357
function 전역신호(시뮬평탄: sim.시뮬결과, 접두: string, 노드값: netlist.노드) -> sim.노드신호 {358
if netlist.입력노드인가(노드값.종류) {359
{ 아이디: 노드값.아이디, 신호: 노드값.값 == 1, 프레임: 0 }360
} else {361
let 전역 = sim.신호로(시뮬평탄, 접두 + 노드값.아이디)362
{ 아이디: 노드값.아이디, 신호: 전역.신호, 프레임: 전역.프레임 }363
}364
}366
// 다시 평가하면 순환에 막히는 단계는 전역 정착 벡터를 투영해 칩 안 래치의 상태를 그대로 보여 줍니다.367
// 전역에 없는 노드는 이 단계의 extout 터미널이라 구동 전선의 시작 노드 신호로 채웁니다. 앞에서 채운 값을 뒤에서 읽으므로 차례대로 채웁니다.368
// 채우는 동안 아이디는 바뀌지 않으므로 자리표와 구동표는 한 번만 만듭니다.369
function 정착투영(시뮬평탄: sim.시뮬결과, 접두: string, 평탄단계: netlist.회로) -> sim.시뮬결과 {370
let mut 신호들 = 평탄단계.노드들.map(노드값 => 전역신호(시뮬평탄, 접두, 노드값))371
let 자리표 = sim.자리표만들기(신호들)372
let 구동표 = 터미널구동표만들기(평탄단계.전선들)373
for i in 0..<신호들.length {374
if 신호들[i].프레임 < 0 {375
let 원천자리 = 자리표.getOr(구동표.getOr(신호들[i].아이디, 빈끝점).노드, -1)376
신호들[i] = 신호들[i]{ 신호: 원천자리 >= 0 && 신호들[원천자리].신호, 프레임: 0 }377
}378
}379
sim.시뮬만들기(신호들, 1)380
}382
function 경계값넣기(위: 보기단계, 칩: 놓인칩, 노드값: netlist.노드) -> netlist.노드 {383
let 핀 = 외부입력핀(칩.정의, 노드값.아이디)384
if 핀 == "" { return 노드값 }385
let 켜짐 = sim.신호로(위.시뮬, 칩입력원천(위.분해, 칩.배치.아이디, 핀).노드).신호386
노드값{ 값: if 켜짐 { 1 } else { 0 } }387
}389
// 칩 안의 extin 터미널을 바깥 경계 신호로 채워 그 단계만 따로 평가합니다. 고장은 단계마다 접두를 한 겹씩 벗깁니다.390
function 한단계내려가기(위: 보기단계, 칩: 놓인칩, 시뮬평탄: sim.시뮬결과, 접두: string) -> 보기단계 {391
let 칩본체 = 칩.정의.본체392
let 분해값 = 위.분해{ 본체: 칩본체{ 노드들: 칩본체.노드들.map(노드값 => 경계값넣기(위, 칩, 노드값)) } }393
let 고장들 = fault.내부고장(위.고장들, 칩.배치.아이디)394
let 평탄단계 = fault.고장회로(평탄화(분해값), 고장들)395
let 조합 = sim.평가회로(평탄단계)396
{ 분해: 분해값, 시뮬: if sim.되먹임인가(조합) { 정착투영(시뮬평탄, 접두, 평탄단계) } else { 조합 }, 고장들: 고장들 }397
}399
// 경로의 인스턴스마다 한 단계씩 내려갑니다. 빈 경로는 최상위 그대로입니다.400
export function 경로뷰(분해값: 분해결과, 시뮬평탄: sim.시뮬결과, 경로: Array<string>, 고장들: Array<fault.고장>) -> Result<경로보기, string> {401
let mut 단계값: 보기단계 = { 분해: 분해값, 시뮬: 시뮬평탄, 고장들: 고장들 }402
let mut 이름들: Array<string> = []403
for i in 0..<경로.length {404
let 칩 = match 놓인칩찾기(단계값.분해, 경로[i]) {405
case Some(찾은칩) => 찾은칩406
case None => return Err("뷰가 없는 인스턴스를 가리킵니다: " + 경로[i])407
}408
이름들.push(칩.배치.칩이름)409
단계값 = 한단계내려가기(단계값, 칩, 시뮬평탄, 경로.slice(0, i + 1).map(아이디 => 아이디 + "__").join(""))410
}411
Ok({ 단계: 단계값, 이름들: 이름들 })412
}SHA256 431fbef82fa0a3c9c6b80b1230e4bdb3170b75f7350c5510afbb2c8833cf3d72