slow.monster

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.
7import core.netlist
8import core.sim
9import core.fault
10import core.util
12export type 외부핀 = { 핀: string, 노드: string }
13export type 인스턴스 = { 아이디: string, 칩이름: string, x: int, y: int }
14export type 끝점 = { 노드: string, 핀: string }
15// 표들은 전선과 핀을 찾을 때마다 배열을 훑지 않도록 한 번 만든 색인입니다. 같은 키가 여럿이면 배열의 마지막 것이 남습니다.
16// 구동표는 끝 노드:끝 핀에서 그 핀을 구동하는 시작 끝점으로 갑니다. 전선 끝점에는 : 가 없어 키가 겹치지 않습니다.
17export type 회로본체 = { 노드들: Array<netlist.노드>, 전선들: Array<netlist.전선>, 인스턴스들: Array<인스턴스>, 구동표: Map<string, 끝점> }
18// 본체는 펼치기 전 구조이고 내부는 펼친 원시 회로입니다. 내보내기와 칩 안 보기는 본체를 씁니다.
19// 외부입력표는 내부 노드에서 extin 핀으로, 외부출력표는 extout 핀에서 내부 노드로, 터미널구동표는 내부 끝 노드에서 그 노드를 구동하는 끝점으로 갑니다.
20export type 칩정의 = {
21 이름: string,
22 본체: 회로본체,
23 내부: netlist.회로,
24 외부입력: Array<외부핀>,
25 외부출력: Array<외부핀>,
26 외부입력표: Map<string, string>,
27 외부출력표: Map<string, string>,
28 출력터미널들: Set<string>,
29 터미널구동표: Map<string, 끝점>,
31export type 분해결과 = { 정의들: Array<칩정의>, 정의표: Map<string, 칩정의>, 본체: 회로본체 }
32export type 놓인칩 = { 배치: 인스턴스, 정의: 칩정의 }
33export type 보기단계 = { 분해: 분해결과, 시뮬: sim.시뮬결과, 고장들: Array<fault.고장> }
34export type 경로보기 = { 단계: 보기단계, 이름들: Array<string> }
36type 줄머리 = "chipdef" | "endchip" | "node" | "wire" | "chip" | "extin" | "extout" | "기타"
37type 핀쪽 = "입력" | "출력"
38enum 본체항목 { 노드줄(Array<string>), 전선줄(netlist.전선), 칩줄(인스턴스), 외부입력줄(외부핀), 외부출력줄(외부핀) }
39// node 줄은 앞에서 정의한 칩이 모두 모인 뒤 netlist 가 검증하므로 토큰으로 둡니다.
40type 생본체 = {
41 노드줄들: Array<Array<string>>,
42 전선들: Array<netlist.전선>,
43 인스턴스들: Array<인스턴스>,
44 외부입력: Array<외부핀>,
45 외부출력: Array<외부핀>,
47type 생칩정의 = { 이름: string, 본체: 생본체 }
49let 빈끝점: 끝점 = { 노드: "", 핀: "" }
50let 빈정의: 칩정의 = {
51 이름: "",
52 본체: { 노드들: [], 전선들: [], 인스턴스들: [], 구동표: Map.new() },
53 내부: { 노드들: [], 전선들: [] },
54 외부입력: [],
55 외부출력: [],
56 외부입력표: Map.new(),
57 외부출력표: Map.new(),
58 출력터미널들: Set.of(),
59 터미널구동표: Map.new(),
62function 이름공간(칩아이디: string, 노드아이디: string) -> string { 칩아이디 + "__" + 노드아이디 }
64function 구동표만들기(전선들: Array<netlist.전선>) -> Map<string, 끝점> {
65 let mut 표: Map<string, 끝점> = Map.new()
66 for 선 in 전선들 { 표.insert(선.끝노드 + ":" + 선.끝핀, { 노드: 선.시작노드, 핀: 선.시작핀 }) }
67 표
70// extout 터미널은 OUTPUT 이라 in0 하나만 받으므로 끝 노드만 키로 씁니다.
71function 터미널구동표만들기(전선들: Array<netlist.전선>) -> Map<string, 끝점> {
72 let mut 표: Map<string, 끝점> = Map.new()
73 for 선 in 전선들 { 표.insert(선.끝노드, { 노드: 선.시작노드, 핀: 선.시작핀 }) }
74 표
77function 노드에서핀표(핀들: Array<외부핀>) -> Map<string, string> {
78 let mut 표: Map<string, string> = Map.new()
79 for 핀값 in 핀들 { 표.insert(핀값.노드, 핀값.핀) }
80 표
83function 핀에서노드표(핀들: Array<외부핀>) -> Map<string, string> {
84 let mut 표: Map<string, string> = Map.new()
85 for 핀값 in 핀들 { 표.insert(핀값.핀, 핀값.노드) }
86 표
89function 정의표만들기(정의들: Array<칩정의>) -> Map<string, 칩정의> {
90 let mut 표: Map<string, 칩정의> = Map.new()
91 for 정의 in 정의들 { 표.insert(정의.이름, 정의) }
92 표
95function 분해결과만들기(정의들: Array<칩정의>, 본체값: 회로본체) -> 분해결과 {
96 { 정의들: 정의들, 정의표: 정의표만들기(정의들), 본체: 본체값 }
99function 정의찾기(정의표: Map<string, 칩정의>, 이름: string) -> 칩정의 {
100 정의표.getOr(이름, 빈정의)
103function 놓인칩으로(정의표: Map<string, 칩정의>, 배치: 인스턴스) -> 놓인칩 {
104 { 배치: 배치, 정의: 정의찾기(정의표, 배치.칩이름) }
107// 같은 아이디의 인스턴스가 여럿이면 마지막 것을 씁니다.
108function 칩찾기(정의표: Map<string, 칩정의>, 인스턴스들: Array<인스턴스>, 아이디: string) -> Option<놓인칩> {
109 let 맞는배치들 = 인스턴스들.filter(배치 => 배치.아이디 == 아이디)
110 if 맞는배치들.length == 0 { None } else { Some(놓인칩으로(정의표, 맞는배치들[맞는배치들.length - 1])) }
113export function 놓인칩찾기(분해값: 분해결과, 아이디: string) -> Option<놓인칩> {
114 칩찾기(분해값.정의표, 분해값.본체.인스턴스들, 아이디)
117export function 놓인칩들(분해값: 분해결과) -> Array<놓인칩> {
118 분해값.본체.인스턴스들.map(배치 => 놓인칩으로(분해값.정의표, 배치))
121// 한 내부 노드에 외부 핀이 여럿 붙으면 마지막 핀을 씁니다.
122function 외부입력핀(정의: 칩정의, 노드아이디: string) -> string {
123 정의.외부입력표.getOr(노드아이디, "")
126function 출력터미널인가(정의: 칩정의, 노드아이디: string) -> bool {
127 노드아이디 in 정의.출력터미널들
130// 같은 노드에 외부 출력 핀이 여럿 붙어도 존재만 묻습니다. 칩 정의를 만들 때 한 번 모읍니다.
131function 출력터미널모으기(핀들: Array<외부핀>) -> Set<string> {
132 let mut 노드들: Set<string> = Set.of()
133 for 핀값 in 핀들 { 노드들.add(핀값.노드) }
134 노드들
137function 터미널인가(정의: 칩정의, 노드아이디: string) -> bool {
138 외부입력핀(정의, 노드아이디) != "" || 출력터미널인가(정의, 노드아이디)
141function 구동자(본체값: 회로본체, 노드: string, 핀: string) -> 끝점 {
142 본체값.구동표.getOr(노드 + ":" + 핀, 빈끝점)
145function 시작바꾸기(선: netlist.전선, 원천: 끝점) -> netlist.전선 {
146 선{ 시작노드: 원천.노드, 시작핀: 원천.핀 }
149// 칩 출력 핀을 칩 안에서 그 핀을 구동하는 원시 노드로 바꿉니다. 칩 정의는 이미 평탄하고 인스턴스 아이디에는 __ 가 없어 한 번만 내려가면 됩니다.
150export function 평탄원천(분해값: 분해결과, 노드: string, 핀: string) -> 끝점 {
151 match 놓인칩찾기(분해값, 노드) {
152 case None => ({ 노드: 노드, 핀: 핀 })
153 case Some(칩) => {
154 let 터미널 = 칩.정의.외부출력표.getOr(핀, "")
155 let 원천 = 터미널구동자(칩.정의, 터미널)
156 if 터미널 == "" || 원천.노드 == "" { 빈끝점 } else { { 노드: 이름공간(노드, 원천.노드), 핀: 원천.핀 } }
157 }
158 }
161function 터미널구동자(정의: 칩정의, 노드: string) -> 끝점 {
162 정의.터미널구동표.getOr(노드, 빈끝점)
165// 칩 입력 핀의 신호는 바깥에서 그 핀을 구동하는 원시 노드가 들고 있습니다.
166export function 칩입력원천(분해값: 분해결과, 칩아이디: string, 핀: string) -> 끝점 {
167 let 구동 = 구동자(분해값.본체, 칩아이디, 핀)
168 평탄원천(분해값, 구동.노드, 구동.핀)
171// extin 터미널에서 나가는 칩 안 전선은 바깥 구동자에 바로 잇고, extout 터미널로 가는 전선은 바깥쪽 전선이 대신합니다.
172function 칩안전선(분해값: 분해결과, 칩: 놓인칩, 선: netlist.전선) -> netlist.전선 {
173 let 입력핀 = 외부입력핀(칩.정의, 선.시작노드)
174 let 원천 = if 입력핀 == "" { { 노드: 이름공간(칩.배치.아이디, 선.시작노드), 핀: 선.시작핀 } } else { 칩입력원천(분해값, 칩.배치.아이디, 입력핀) }
175 { 시작노드: 원천.노드, 시작핀: 원천.핀, 끝노드: 이름공간(칩.배치.아이디, 선.끝노드), 끝핀: 선.끝핀 }
178// 본체 노드, 칩마다 터미널을 뺀 내부 노드, 원시 사이 전선, 칩 안 전선, 칩 출력에서 나가는 전선 순서입니다.
179// 이 순서가 신호 벡터와 정착 상태 문자열의 순서이므로 바꾸지 않습니다.
180export 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 }
198function 줄머리로(토큰: 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 }
211function 알수없는줄(칩안: bool, 머리: string) -> string {
212 if 칩안 { "칩 정의 안의 알 수 없는 줄입니다: " + 머리 } else { "알 수 없는 줄입니다: " + 머리 }
215function 칩머리읽기(토큰들: Array<string>) -> Result<string, string> {
216 if 토큰들.length != 2 { return Err("chipdef 줄 형식은 chipdef 이름 입니다") }
217 if !util.안전아이디인가(토큰들[1]) { return Err("유효하지 않은 칩 이름입니다: " + 토큰들[1]) }
218 Ok(토큰들[1])
221function 인스턴스줄파싱(토큰들: 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 })
229// 토큰이 셋이 아닌 extin 과 extout 은 알 수 없는 줄로 봅니다.
230function 외부핀줄읽기(칩안: 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 { 본체항목.외부출력줄(핀값) })
237function 본체줄읽기(칩안: 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 }
247function 본체모으기(항목들: 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 { 노드줄들: 노드줄들, 전선들: 전선들, 인스턴스들: 인스턴스들, 외부입력: 외부입력, 외부출력: 외부출력 }
265function 칩핀없음인가(정의표: Map<string, 칩정의>, 인스턴스들: Array<인스턴스>, 노드: string, 핀: string, 쪽: 핀쪽) -> bool {
266 match 칩찾기(정의표, 인스턴스들, 노드) {
267 case None => false
268 case Some(칩) => {
269 let 핀들 = match 쪽 {
270 case "입력" => 칩.정의.외부입력
271 case "출력" => 칩.정의.외부출력
272 }
273 핀들.filter(핀값 => 핀값.핀 == 핀).length == 0
274 }
275 }
278// 원시 노드끼리의 전선은 netlist 가 검증하고, 칩에 닿는 전선은 그 칩이 선언한 extin 이나 extout 핀이어야 합니다.
279function 본체검증(정의들: 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({ 노드들: 원시회로.노드들, 전선들: 생본체값.전선들, 인스턴스들: 생본체값.인스턴스들, 구동표: 구동표만들기(생본체값.전선들) })
293function 정의만들기(앞정의들: 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 })
318// 인스턴스 이름공간이 부모 노드와 부딪히는 경우를 펼친 뒤에 잡습니다.
319function 충돌검사(분해값: 분해결과) -> Result<분해결과, string> {
320 let mut 본아이디들: Set<string> = Set.of()
321 for 노드값 in 평탄화(분해값).노드들 {
322 if 노드값.아이디 in 본아이디들 { return Err("노드 아이디가 충돌합니다: " + 노드값.아이디) }
323 본아이디들.add(노드값.아이디)
324 }
325 Ok(분해값)
328// 줄 오류를 먼저 모두 보고, 그다음 칩 정의를 정의 순서로, 마지막으로 최상위 본체를 검증합니다. 첫 오류에서 멈춥니다.
329export function 분해(원문: string) -> Result<분해결과, string> {
330 let mut 생정의들: Array<생칩정의> = []
331 let mut 최상위항목들: Array<본체항목> = []
332 let mut 칩항목들: Array<본체항목> = []
333 let mut 칩이름 = ""
334 let mut 칩안 = false
335 for 토큰들 in 원문.split("\n").map(util.토큰나누기).filter(줄토큰들 => 줄토큰들.length > 0) {
336 let 머리 = 줄머리로(토큰들[0])
337 if 칩안 && 머리 == "endchip" {
338 생정의들.push({ 이름: 칩이름, 본체: 본체모으기(칩항목들) })
339 칩안 = false
340 } else if !칩안 && 머리 == "chipdef" {
341 칩이름 = 칩머리읽기(토큰들)?
342 칩항목들 = []
343 칩안 = true
344 } 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 충돌검사(분해결과만들기(정의들, 본체값))
357function 전역신호(시뮬평탄: sim.시뮬결과, 접두: string, 노드값: netlist.노드) -> sim.노드신호 {
358 if netlist.입력노드인가(노드값.종류) {
359 { 아이디: 노드값.아이디, 신호: 노드값.값 == 1, 프레임: 0 }
360 } else {
361 let 전역 = sim.신호로(시뮬평탄, 접두 + 노드값.아이디)
362 { 아이디: 노드값.아이디, 신호: 전역.신호, 프레임: 전역.프레임 }
363 }
366// 다시 평가하면 순환에 막히는 단계는 전역 정착 벡터를 투영해 칩 안 래치의 상태를 그대로 보여 줍니다.
367// 전역에 없는 노드는 이 단계의 extout 터미널이라 구동 전선의 시작 노드 신호로 채웁니다. 앞에서 채운 값을 뒤에서 읽으므로 차례대로 채웁니다.
368// 채우는 동안 아이디는 바뀌지 않으므로 자리표와 구동표는 한 번만 만듭니다.
369function 정착투영(시뮬평탄: 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)
382function 경계값넣기(위: 보기단계, 칩: 놓인칩, 노드값: netlist.노드) -> netlist.노드 {
383 let 핀 = 외부입력핀(칩.정의, 노드값.아이디)
384 if 핀 == "" { return 노드값 }
385 let 켜짐 = sim.신호로(위.시뮬, 칩입력원천(위.분해, 칩.배치.아이디, 핀).노드).신호
386 노드값{ 값: if 켜짐 { 1 } else { 0 } }
389// 칩 안의 extin 터미널을 바깥 경계 신호로 채워 그 단계만 따로 평가합니다. 고장은 단계마다 접두를 한 겹씩 벗깁니다.
390function 한단계내려가기(위: 보기단계, 칩: 놓인칩, 시뮬평탄: sim.시뮬결과, 접두: string) -> 보기단계 {
391 let 칩본체 = 칩.정의.본체
392 let 분해값 = 위.분해{ 본체: 칩본체{ 노드들: 칩본체.노드들.map(노드값 => 경계값넣기(위, 칩, 노드값)) } }
393 let 고장들 = fault.내부고장(위.고장들, 칩.배치.아이디)
394 let 평탄단계 = fault.고장회로(평탄화(분해값), 고장들)
395 let 조합 = sim.평가회로(평탄단계)
396 { 분해: 분해값, 시뮬: if sim.되먹임인가(조합) { 정착투영(시뮬평탄, 접두, 평탄단계) } else { 조합 }, 고장들: 고장들 }
399// 경로의 인스턴스마다 한 단계씩 내려갑니다. 빈 경로는 최상위 그대로입니다.
400export 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({ 단계: 단계값, 이름들: 이름들 })

SHA256 431fbef82fa0a3c9c6b80b1230e4bdb3170b75f7350c5510afbb2c8833cf3d72