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/sim.tpz

Topaz · 8423 B · Original file

1// 회로 평가. 조합 회로는 위상 순서로 풀고, 되먹임 회로는 단위 지연으로 고정점까지 정착시킵니다.
2// 입력은 평탄한 원시 회로이고 출력은 노드마다 신호와 정착 프레임, 정착 상태와 스텝 수입니다.
3// 같은 회로와 같은 이전 상태면 늘 같은 신호가 나옵니다. 게이트 출력은 core.gate 진리표로만 계산합니다.
4// 칩 계층을 펼치지 않습니다. 그 일은 core.hierarchy 가 합니다.
5// Circuit evaluation: topological layers for combinational circuits, unit-delay settling for feedback.
7import core.netlist
9export type 노드신호 = { 아이디: string, 신호: bool, 프레임: int }
10// 자리표는 아이디에서 신호들의 자리로 가는 색인입니다. 신호를 찾을 때마다 배열을 훑으면 큰 회로에서 제곱 시간이 됩니다.
11export type 시뮬결과 = { 신호들: Array<노드신호>, 프레임수: int, 자리표: Map<string, int> }
12export type 정착상태 = "settled" | "oscillating" | "unsettled"
13export type 정착결과 = { 시뮬: 시뮬결과, 상태: 정착상태, 스텝: int }
15// 노드 순서와 나란한 배열로, 각 노드의 in0 과 in1 을 구동하는 노드의 자리입니다. 구동이 없으면 -1 입니다.
16type 구동표 = { 입력0: Array<int>, 입력1: Array<int> }
18// 노드마다 전선을 다시 찾으면 큰 회로에서 너무 느려 평가 전에 한 번 만듭니다.
19function 구동표만들기(회로값: netlist.회로) -> 구동표 {
20 let 노드들 = 회로값.노드들
21 let mut 자리표: Map<string, int> = Map.new()
22 for i in 0..<노드들.length { 자리표.insert(노드들[i].아이디, i) }
23 let mut 입력0 = [for i in 0..<노드들.length => -1]
24 let mut 입력1 = [for i in 0..<노드들.length => -1]
25 for 선 in 회로값.전선들 {
26 let 끝자리 = 자리표.getOr(선.끝노드, -1)
27 if 끝자리 >= 0 && 선.끝핀 == "in0" { 입력0[끝자리] = 자리표.getOr(선.시작노드, -1) }
28 if 끝자리 >= 0 && 선.끝핀 == "in1" { 입력1[끝자리] = 자리표.getOr(선.시작노드, -1) }
29 }
30 { 입력0: 입력0, 입력1: 입력1 }
33// 연결되지 않은 입력은 0 으로 봅니다.
34function 원천신호(신호들: Array<노드신호>, 자리: int) -> bool {
35 자리 >= 0 && 신호들[자리].신호
38function 풀렸는가(신호들: Array<노드신호>, 자리: int) -> bool {
39 자리 < 0 || 신호들[자리].프레임 >= 0
42function 원천프레임(신호들: Array<노드신호>, 자리: int) -> int {
43 if 자리 < 0 { 0 } else { 신호들[자리].프레임 }
46// 앞에서부터 넣으므로 같은 아이디가 여럿이면 마지막 자리가 남습니다.
47export function 자리표만들기(신호들: Array<노드신호>) -> Map<string, int> {
48 let mut 자리표: Map<string, int> = Map.new()
49 for i in 0..<신호들.length { 자리표.insert(신호들[i].아이디, i) }
50 자리표
53export function 시뮬만들기(신호들: Array<노드신호>, 프레임수: int) -> 시뮬결과 {
54 { 신호들: 신호들, 프레임수: 프레임수, 자리표: 자리표만들기(신호들) }
57// 없는 아이디는 꺼진 신호와 프레임 -1 입니다.
58export function 신호로(시뮬: 시뮬결과, 아이디: string) -> 노드신호 {
59 let 자리 = 시뮬.자리표.getOr(아이디, -1)
60 if 자리 < 0 { { 아이디: "", 신호: false, 프레임: -1 } } else { 시뮬.신호들[자리] }
63// 위상 평가가 끝까지 풀지 못한 노드는 프레임 -1 로 남으므로, 그런 노드가 있으면 되먹임이 있는 회로입니다.
64export function 되먹임인가(시뮬: 시뮬결과) -> bool {
65 시뮬.신호들.filter(신호값 => 신호값.프레임 < 0).length > 0
68// 층마다 입력이 모두 풀린 노드를 풉니다. 프레임은 가장 긴 경로의 깊이이고 OUTPUT 은 원천의 프레임을 그대로 씁니다.
69// SEG7 은 입력이 일곱이지만 준비와 프레임은 in0 과 in1 만 봅니다.
70export function 평가회로(회로값: netlist.회로) -> 시뮬결과 {
71 let 노드들 = 회로값.노드들
72 let 구동 = 구동표만들기(회로값)
73 let mut 신호들 = 노드들.map(노드값 => if netlist.입력노드인가(노드값.종류) {
74 { 아이디: 노드값.아이디, 신호: 노드값.값 == 1, 프레임: 0 }
75 } else {
76 { 아이디: 노드값.아이디, 신호: false, 프레임: -1 }
77 })
78 let mut 남은수 = 신호들.filter(신호값 => 신호값.프레임 < 0).length
79 let mut 진전 = true
80 while 남은수 > 0 && 진전 {
81 진전 = false
82 for i in 0..<노드들.length {
83 let 원0 = 구동.입력0[i]
84 let 원1 = 구동.입력1[i]
85 let 종류 = 노드들[i].종류
86 let 한입력 = netlist.입력핀들(종류).length <= 1
87 if 신호들[i].프레임 < 0 && 풀렸는가(신호들, 원0) && (한입력 || 풀렸는가(신호들, 원1)) {
88 let 프레임0 = 원천프레임(신호들, 원0)
89 let 프레임 = if netlist.출력노드인가(종류) { 프레임0 } else if 한입력 { 프레임0 + 1 } else { 프레임0.atLeast(원천프레임(신호들, 원1)) + 1 }
90 신호들[i] = { 아이디: 노드들[i].아이디, 신호: netlist.출력계산(종류, 원천신호(신호들, 원0), 원천신호(신호들, 원1)), 프레임: 프레임 }
91 남은수 -= 1
92 진전 = true
93 }
94 }
95 }
96 시뮬만들기(신호들, 신호들.reduce(0, (최대, 신호값) => 최대.atLeast(신호값.프레임)) + 1)
99// 한 스텝은 앞 스냅숏만 읽어 모든 노드를 새로 계산합니다. 제자리에서 바꾸면 갱신 순서가 결과를 바꿉니다.
100function 한스텝(노드들: Array<netlist.노드>, 구동: 구동표, 현재: Array<노드신호>) -> Array<노드신호> {
101 [for i in 0..<노드들.length => ({
102 아이디: 노드들[i].아이디,
103 신호: if netlist.입력노드인가(노드들[i].종류) { 노드들[i].값 == 1 } else { netlist.출력계산(노드들[i].종류, 원천신호(현재, 구동.입력0[i]), 원천신호(현재, 구동.입력1[i])) },
104 프레임: 0,
105 })]
108function 벡터키(신호들: Array<노드신호>) -> string {
109 신호들.map(신호값 => if 신호값.신호 { "1" } else { "0" }).join("")
112// 비순환 회로는 위상 평가를 그대로 돌려줘 조합 결과와 바이트까지 같습니다.
113// 되먹임 회로는 이전 정착 상태에서 시작해 변화가 멈추면 정착, 본 벡터가 다시 나오면 발진, 스텝 한도를 넘으면 미정착입니다.
114// 프레임은 노드 값이 마지막으로 바뀐 스텝이라 정착 순서대로 신호가 드러납니다.
115export function 정착평가회로(회로값: netlist.회로, 이전상태: Array<노드신호>, 최대스텝: int) -> 정착결과 {
116 let 조합 = 평가회로(회로값)
117 if !되먹임인가(조합) { return { 시뮬: 조합, 상태: "settled", 스텝: 조합.프레임수 } }
118 let 노드들 = 회로값.노드들
119 let 구동 = 구동표만들기(회로값)
120 let mut 이전신호표: Map<string, bool> = Map.new()
121 for 신호값 in 이전상태 { 이전신호표.insert(신호값.아이디, 신호값.신호) }
122 let mut 현재 = 노드들.map(노드값 => ({
123 아이디: 노드값.아이디,
124 신호: if netlist.입력노드인가(노드값.종류) { 노드값.값 == 1 } else { 이전신호표.getOr(노드값.아이디, false) },
125 프레임: 0,
126 }))
127 let mut 현재키 = 벡터키(현재)
128 let mut 본키들: Set<string> = Set.of(현재키)
129 let mut 마지막변경 = [for i in 0..<노드들.length => 0]
130 let mut 상태: 정착상태 = "unsettled"
131 let mut 스텝 = 0
132 while 스텝 < 최대스텝 && 상태 == "unsettled" {
133 let 다음 = 한스텝(노드들, 구동, 현재)
134 스텝 += 1
135 for i in 0..<다음.length {
136 if 다음[i].신호 != 현재[i].신호 { 마지막변경[i] = 스텝 }
137 }
138 let 다음키 = 벡터키(다음)
139 if 다음키 == 현재키 {
140 상태 = "settled"
141 } else if 다음키 in 본키들 {
142 상태 = "oscillating"
143 } else {
144 본키들.add(다음키)
145 }
146 현재 = 다음
147 현재키 = 다음키
148 }
149 let 신호들 = [for i in 0..<현재.length => 현재[i]{ 프레임: 마지막변경[i] }]
150 let 최대프레임 = 마지막변경.reduce(0, (최대, 변경) => 최대.atLeast(변경))
151 { 시뮬: 시뮬만들기(신호들, 최대프레임 + 1), 상태: 상태, 스텝: 스텝 }

SHA256 25be31a4fe115758928dd1901f88c66db06fd9d281b7f8765be895fd31a2ea0d