A python module for parsing, processing, and simulating gate-level circuits.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1874 lines
57 KiB

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# KyuPy Introduction\n",
"\n",
"Working with KyuPy's basic data structures.\n",
"\n",
"## Gate-Level Circuits\n",
"\n",
"KyuPy has parsers for:\n",
"\n",
"* The [ISCAS'89 Benchmark Format](https://www.researchgate.net/profile/Franc-Brglez/publication/224723140_Combination_profiles_of_sequential_benchmark_circuits) \".bench\"\n",
"* Non-hierarchical gate-level verilog\n",
"\n",
"Files can be loaded using `load(file)`, strings can be parsed using `parse(text)`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# 0000000.000 W Numba unavailable. Falling back to pure Python.\n"
]
}
],
"source": [
"from kyupy import bench, verilog\n",
"\n",
"# load a file\n",
"b14 = verilog.load('../tests/b14.v.gz')\n",
"\n",
"# ... or specify the circuit as string \n",
"adder = bench.parse('''\n",
"INPUT(a, b)\n",
"OUTPUT(s)\n",
"cin = DFF(cout)\n",
"axb = XOR(a, b)\n",
"s = XOR(axb, cin)\n",
"aab = AND(a, b)\n",
"axbacin = AND(axb, cin)\n",
"cout = OR(aab, axbacin)\n",
"''', name='adder')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"They return KyuPy's intermediate prepresentation of the circuit graph (objects of class `Circuit`):"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{name: \"b14\", cells: 15873, forks: 15842, lines: 46891, io_nodes: 91}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b14"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{name: \"adder\", cells: 6, forks: 8, lines: 17, io_nodes: 3}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Apparently, circuits contain `cells`, `forks`, `lines`, and `io_nodes`.\n",
"\n",
"### Cells and Forks\n",
"\n",
"Let's explore cells and forks for the adder circuit.\n",
"\n",
"There are dictionary-mappings from names to these objects:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'cin': 4:DFF\"cin\" <1 >0,\n",
" 'axb': 6:XOR\"axb\" <3 <4 >2,\n",
" 's': 8:XOR\"s\" <6 <7 >5,\n",
" 'aab': 9:AND\"aab\" <9 <10 >8,\n",
" 'axbacin': 11:AND\"axbacin\" <12 <13 >11,\n",
" 'cout': 13:OR\"cout\" <15 <16 >14}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.cells"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'a': 0:__fork__\"a\" >3 >9,\n",
" 'b': 1:__fork__\"b\" >4 >10,\n",
" 's': 2:__fork__\"s\" <5 ,\n",
" 'cout': 3:__fork__\"cout\" <14 >1,\n",
" 'cin': 5:__fork__\"cin\" <0 >7 >13,\n",
" 'axb': 7:__fork__\"axb\" <2 >6 >12,\n",
" 'aab': 10:__fork__\"aab\" <8 >15,\n",
" 'axbacin': 12:__fork__\"axbacin\" <11 >16}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.forks"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(For bench-files, the names of gates equal the names of the signals they produce. In verilog files, the names can be different.)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6:XOR\"axb\" <3 <4 >2"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.cells['axb']"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"7:__fork__\"axb\" <2 >6 >12"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.forks['axb']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cells and forks are instances of class `Node`, which represent *things* that are connected to one or more other *things* in the circuit.\n",
"\n",
"* A **cell** represents a gate or a standard cell.\n",
"* A **fork** represents a named signal or a fan-out point (connecting the output of one cell to multiple other cells or forks).\n",
"\n",
"`Node`-objects have an `index`, a `kind`, and a `name`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(6, 'XOR', 'axb')"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.cells['axb'].index, adder.cells['axb'].kind, adder.cells['axb'].name"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*Forks* are `Node`-objects of the special kind `__fork__`.\n",
"\n",
"*Cells* are `Node`-objects of any other kind. A kind is just a string and can be anything.\n",
"\n",
"The namespaces of *forks* and *cells* are separate:\n",
"* A *cell* and a *fork* **can** have the same name.\n",
"* Two *cells* or two *forks* **cannot** have the same name."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(7, '__fork__', 'axb')"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.forks['axb'].index, adder.forks['axb'].kind, adder.forks['axb'].name"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `index` of a *node* in a circuit is unique and consecutive.\n",
"\n",
"Also *forks* and *cells* have all separate indices.\n",
"\n",
"Nodes can be accessed by their index using the `nodes` list:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0:__fork__\"a\" >3 >9,\n",
" 1:__fork__\"b\" >4 >10,\n",
" 2:__fork__\"s\" <5 ,\n",
" 3:__fork__\"cout\" <14 >1,\n",
" 4:DFF\"cin\" <1 >0,\n",
" 5:__fork__\"cin\" <0 >7 >13,\n",
" 6:XOR\"axb\" <3 <4 >2,\n",
" 7:__fork__\"axb\" <2 >6 >12,\n",
" 8:XOR\"s\" <6 <7 >5,\n",
" 9:AND\"aab\" <9 <10 >8,\n",
" 10:__fork__\"aab\" <8 >15,\n",
" 11:AND\"axbacin\" <12 <13 >11,\n",
" 12:__fork__\"axbacin\" <11 >16,\n",
" 13:OR\"cout\" <15 <16 >14]"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.nodes"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(6:XOR\"axb\" <3 <4 >2, 7:__fork__\"axb\" <2 >6 >12)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.nodes[6], adder.nodes[7]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Lines\n",
"\n",
"A `Line` is a directional 1:1 connection between two Nodes.\n",
"\n",
"A line has a circuit-unique and consecutive `index` just like nodes.\n",
"\n",
"Line and node indices are different!\n",
"\n",
"There is a `lines` list. If a line is printed, it just outputs its index:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.lines"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A line one `driver`-node and one `reader`-node:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(6:XOR\"axb\" <3 <4 >2, 7:__fork__\"axb\" <2 >6 >12)"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.lines[2].driver, adder.lines[2].reader"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nodes show their connections to the lines with direction (\"<\" for input, \">\" for output) and the line index.\n",
"\n",
"In the example above, line 2 connects the output of cell \"axb\" to the input of fork \"axb\".\n",
"\n",
"The input connections and output connections of a node are ordered lists of lines called `ins` and `outs`:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"([3, 4], [2])"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.cells['axb'].ins, adder.cells['axb'].outs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A line also stores its positions in the connection lists in `driver_pin` and `reader_pin`:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0, 0, 1)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.lines[2].driver_pin, adder.lines[2].reader_pin, adder.lines[4].reader_pin"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### IO_Nodes\n",
"\n",
"Any node in the circuit can be designated as a primary input or primary output by adding it to the `io_nodes` list:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0:__fork__\"a\" >3 >9, 1:__fork__\"b\" >4 >10, 2:__fork__\"s\" <5 ]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.io_nodes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is common that io_nodes either have only output connections (in a role as primary-input) or only input connections (in a role as primary-output).\n",
"\n",
"Inputs and outputs appear in the order they were defined in the loaded file. Inputs and outputs are often interspersed.\n",
"\n",
"A related list is `s_nodes`. It contains the io_nodes at the beginning and adds all sequential elements (flip-flops, latches)."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0:__fork__\"a\" >3 >9,\n",
" 1:__fork__\"b\" >4 >10,\n",
" 2:__fork__\"s\" <5 ,\n",
" 4:DFF\"cin\" <1 >0]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.s_nodes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Basic Circuit Navigation\n",
"\n",
"A circuit can be traversed easily using the properties of `Circuit`, `Node`, and `Line`."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6:XOR\"axb\" <3 <4 >2"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.io_nodes[0].outs[0].reader"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6:XOR\"axb\" <3 <4 >2\n",
"9:AND\"aab\" <9 <10 >8\n"
]
}
],
"source": [
"for line in adder.io_nodes[0].outs:\n",
" print(line.reader)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'cout'"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.cells['cin'].ins[0].driver.name"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's continue with `b14` loaded before. It has 91 io_nodes:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"({name: \"b14\", cells: 15873, forks: 15842, lines: 46891, io_nodes: 91},\n",
" [31587:input\"clock\" >15805,\n",
" 31589:input\"reset\" >15806,\n",
" 31591:output\"addr[19]\" <46836 ,\n",
" 31592:output\"addr[18]\" <46837 ,\n",
" 31593:output\"addr[17]\" <46838 ,\n",
" 31594:output\"addr[16]\" <46839 ,\n",
" 31595:output\"addr[15]\" <46840 ,\n",
" 31596:output\"addr[14]\" <46841 ,\n",
" 31597:output\"addr[13]\" <46842 ,\n",
" 31598:output\"addr[12]\" <46843 ,\n",
" 31599:output\"addr[11]\" <46844 ,\n",
" 31600:output\"addr[10]\" <46845 ,\n",
" 31601:output\"addr[9]\" <46846 ,\n",
" 31602:output\"addr[8]\" <46847 ,\n",
" 31603:output\"addr[7]\" <46848 ,\n",
" 31604:output\"addr[6]\" <46849 ,\n",
" 31605:output\"addr[5]\" <46850 ,\n",
" 31606:output\"addr[4]\" <46851 ,\n",
" 31607:output\"addr[3]\" <46852 ,\n",
" 31608:output\"addr[2]\" <46853 ])"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b14, b14.io_nodes[:20]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and even more sequential nodes:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"306"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(b14.s_nodes)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `io_locs(prefix)` and `s_locs(prefix)` methods return the locations of signals, busses and registers in `io_nodes` and `s_nodes`:"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b14.io_locs('reset')"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2]"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b14.io_locs('addr')"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1130:SDFFARX1_RVT\"IR_reg_0_\" <16917 <16919 <16918 <16920 <16921 >566 >567,\n",
" 1202:SDFFARX1_RVT\"IR_reg_1_\" <17052 <17054 <17053 <17055 <17056 >611 >612,\n",
" 1124:SDFFARX1_RVT\"IR_reg_2_\" <16907 <16909 <16908 <16910 <16911 >562 >563,\n",
" 1127:SDFFARX1_RVT\"IR_reg_3_\" <16912 <16914 <16913 <16915 <16916 >564 >565,\n",
" 1199:SDFFARX1_RVT\"IR_reg_4_\" <17047 <17049 <17048 <17050 <17051 >609 >610,\n",
" 1196:SDFFARX1_RVT\"IR_reg_5_\" <17042 <17044 <17043 <17045 <17046 >607 >608,\n",
" 1193:SDFFARX1_RVT\"IR_reg_6_\" <17037 <17039 <17038 <17040 <17041 >605 >606,\n",
" 1190:SDFFARX1_RVT\"IR_reg_7_\" <17032 <17034 <17033 <17035 <17036 >603 >604,\n",
" 1187:SDFFARX1_RVT\"IR_reg_8_\" <17027 <17029 <17028 <17030 <17031 >601 >602,\n",
" 1184:SDFFARX1_RVT\"IR_reg_9_\" <17022 <17024 <17023 <17025 <17026 >599 >600,\n",
" 1181:SDFFARX1_RVT\"IR_reg_10_\" <17017 <17019 <17018 <17020 <17021 >597 >598,\n",
" 1178:SDFFARX1_RVT\"IR_reg_11_\" <17012 <17014 <17013 <17015 <17016 >595 >596,\n",
" 1175:SDFFARX1_RVT\"IR_reg_12_\" <17007 <17009 <17008 <17010 <17011 >593 >594,\n",
" 1172:SDFFARX1_RVT\"IR_reg_13_\" <17002 <17004 <17003 <17005 <17006 >591 >592,\n",
" 1169:SDFFARX1_RVT\"IR_reg_14_\" <16997 <16999 <16998 <17000 <17001 >589 >590,\n",
" 1166:SDFFARX1_RVT\"IR_reg_15_\" <16992 <16994 <16993 <16995 <16996 >587 >588,\n",
" 1163:SDFFARX1_RVT\"IR_reg_16_\" <16987 <16989 <16988 <16990 <16991 >585 >586,\n",
" 1133:SDFFARX1_RVT\"IR_reg_17_\" <16922 <16924 <16923 <16925 <16926 >568 >569,\n",
" 1136:SDFFARX1_RVT\"IR_reg_18_\" <16927 <16929 <16928 <16930 <16931 >570,\n",
" 1138:SDFFARX1_RVT\"IR_reg_19_\" <16932 <16934 <16933 <16935 <16936 >571 >572,\n",
" 1141:SDFFARX1_RVT\"IR_reg_20_\" <16937 <16939 <16938 <16940 <16941 >573,\n",
" 1143:SDFFARX1_RVT\"IR_reg_21_\" <16942 <16944 <16943 <16945 <16946 >574 >575,\n",
" 1146:SDFFARX1_RVT\"IR_reg_22_\" <16947 <16949 <16948 <16950 <16951 >576 >577,\n",
" 1149:SDFFARX1_RVT\"IR_reg_23_\" <16952 <16954 <16953 <16955 <16956 >578,\n",
" 1151:SDFFARX1_RVT\"IR_reg_24_\" <16957 <16959 <16958 <16960 <16961 >579,\n",
" 1153:SDFFARX1_RVT\"IR_reg_25_\" <16962 <16964 <16963 <16965 <16966 >580,\n",
" 1155:SDFFARX1_RVT\"IR_reg_26_\" <16967 <16969 <16968 <16970 <16971 >581,\n",
" 1157:SDFFARX1_RVT\"IR_reg_27_\" <16972 <16974 <16973 <16975 <16976 >582,\n",
" 1159:SDFFARX1_RVT\"IR_reg_28_\" <16977 <16979 <16978 <16980 <16981 >583,\n",
" 1161:SDFFARX1_RVT\"IR_reg_29_\" <16982 <16984 <16983 <16985 <16986 >584,\n",
" 1122:SDFFARX1_RVT\"IR_reg_30_\" <16902 <16904 <16903 <16905 <16906 >561,\n",
" 1120:SDFFARX1_RVT\"IR_reg_31_\" <16897 <16899 <16898 <16900 <16901 >560]"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[b14.s_nodes[i] for i in b14.s_locs('IR_reg')]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Example: Tracing out a scan chain.**\n",
"\n",
"We start at the output of the scan chain \"test_so000\", then go backwards through the circuit.\n",
"\n",
"When we encounter a scan cell \"SDFF\", we continue with the \"SI\" pin, which has index 2."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"length (with forks): 573\n",
"length (without forks): 287\n",
"length only SDFF: 215\n",
"output\"test_so000\" __fork__\"test_so000\" NBUFFX8_RVT\"HFSBUF_36_76\" __fork__\"aps_rename_215_\" SDFFARX1_RVT\"wr_reg\" __fork__\"HFSNET_169\" INVX4_RVT\"HFSINV_691_254\" __fork__\"HFSNET_170\" INVX0_RVT\"HFSINV_2682_255\" __fork__\"state\" ... __fork__\"IR[0]\" SDFFARX1_RVT\"IR_reg_0_\" __fork__\"ZBUF_17_16\" NBUFFX2_RVT\"ZBUF_17_inst_905\" __fork__\"ZBUF_275_16\" NBUFFX4_RVT\"ZBUF_275_inst_906\" __fork__\"B\" SDFFARX1_RVT\"B_reg\" __fork__\"test_si000\" input\"test_si000\"\n"
]
}
],
"source": [
"chain = [cell := b14.cells['test_so000']]\n",
"while len(cell.ins) > 0:\n",
" chain.append(cell := cell.ins[2 if cell.kind.startswith('SDFF') else 0].driver)\n",
" \n",
"print(f'length (with forks): {len(chain)}')\n",
"print(f'length (without forks): {len(list(filter(lambda n: n.kind != \"__fork__\", chain)))}')\n",
"print(f'length only SDFF: {len(list(filter(lambda n: n.kind.startswith(\"SDFF\"), chain)))}')\n",
"\n",
"names = [f'{c.kind}\"{c.name}\"' for c in chain]\n",
"print(' '.join(names[:10]) + ' ... ' + ' '.join(names[-10:]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is a generator for **traversing the circuit in topological order**.\n",
"\n",
"The following loop prints all nodes:\n",
"* starting with primary inputs (nodes that don't have any input connections) and sequential elements,\n",
"* and continuing with nodes who's inputs are connected only to already printed nodes."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0:__fork__\"a\" >3 >9\n",
"1:__fork__\"b\" >4 >10\n",
"4:DFF\"cin\" <1 >0\n",
"6:XOR\"axb\" <3 <4 >2\n",
"9:AND\"aab\" <9 <10 >8\n",
"5:__fork__\"cin\" <0 >7 >13\n",
"7:__fork__\"axb\" <2 >6 >12\n",
"10:__fork__\"aab\" <8 >15\n",
"8:XOR\"s\" <6 <7 >5\n",
"11:AND\"axbacin\" <12 <13 >11\n",
"2:__fork__\"s\" <5 \n",
"12:__fork__\"axbacin\" <11 >16\n",
"13:OR\"cout\" <15 <16 >14\n",
"3:__fork__\"cout\" <14 >1\n"
]
}
],
"source": [
"for n in adder.topological_order():\n",
" print(n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Example: Determining logic level (distance from inputs or sequential elements) of nodes.**\n",
"\n",
"Inputs and flip-flops themselves are level 0, *cells* driven by just inputs and flip-flops are level 1, and so on.\n",
"*Fork* nodes have the same level as their driver, because they do not increase the logic depth."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Maximum logic depth: 112\n"
]
}
],
"source": [
"import numpy as np\n",
"\n",
"levels = np.zeros(len(b14.nodes), dtype=np.uint32) # store level for each node.\n",
"\n",
"for n in b14.topological_order():\n",
" if 'DFF' in n.kind or len(n.ins) == 0:\n",
" levels[n] = 0\n",
" elif n.kind == '__fork__':\n",
" levels[n] = levels[n.ins[0].driver] # forks only have exactly one driver\n",
" else:\n",
" levels[n] = max([levels[line.driver] for line in n.ins]) + 1\n",
" \n",
"print(f'Maximum logic depth: {np.max(levels)}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"List nodes with the highest depth and which nodes they are driving."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"depth: 112 node: __fork__ n2692 driving: SDFFARX1_RVT reg1_reg_29_ \n",
"depth: 112 node: NAND2X0_RVT U465 driving: __fork__ n2692 \n",
"depth: 112 node: NAND2X0_RVT U562 driving: __fork__ n2724 \n",
"depth: 112 node: __fork__ n2724 driving: SDFFARX1_RVT reg0_reg_29_ \n",
"depth: 112 node: __fork__ n2608 driving: SDFFARX1_RVT B_reg \n",
"depth: 112 node: NAND2X0_RVT U170 driving: __fork__ n2608 \n",
"depth: 111 node: NAND2X0_RVT U5550 driving: __fork__ n2693 \n",
"depth: 111 node: __fork__ n2660 driving: SDFFARX1_RVT reg2_reg_29_ \n",
"depth: 111 node: AND2X2_RVT U5560 driving: __fork__ n2660 \n",
"depth: 111 node: __fork__ n2725 driving: SDFFARX1_RVT reg0_reg_28_ \n",
"depth: 111 node: __fork__ n2693 driving: SDFFARX1_RVT reg1_reg_28_ \n",
"depth: 111 node: __fork__ n362 driving: NAND2X0_RVT U170 \n",
"depth: 111 node: NAND2X0_RVT U173 driving: __fork__ n362 \n",
"depth: 111 node: __fork__ n600 driving: NAND2X0_RVT U562 \n",
"depth: 111 node: NAND2X0_RVT U563 driving: __fork__ n600 \n",
"depth: 111 node: NAND2X0_RVT U565 driving: __fork__ n2725 \n",
"depth: 111 node: NAND2X0_RVT U466 driving: __fork__ n535 \n",
"depth: 111 node: __fork__ n535 driving: NAND2X0_RVT U465 \n",
"depth: 110 node: __fork__ n4691 driving: AND2X2_RVT U5560 \n",
"depth: 110 node: NAND2X0_RVT U5736 driving: __fork__ n790 \n"
]
}
],
"source": [
"nodes_by_depth = np.argsort(levels)[::-1]\n",
"\n",
"for n_idx in nodes_by_depth[:20]:\n",
" n = b14.nodes[n_idx]\n",
" readers = ', '.join([f'{l.reader.kind:12s} {l.reader.name:14s}' for l in n.outs])\n",
" print(f'depth: {levels[n_idx]} node: {n.kind:12s} {n.name:6s} driving: {readers}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Working With Logic Values\n",
"\n",
"Sequential states of circuits, signals, and test patterns contain logic values.\n",
"\n",
"KyuPy provides some useful tools to deal with 2-valued, 4-valued, and 8-valued logic data.\n",
"\n",
"All logic values are stored in numpy arrays of dtype `np.uint8`.\n",
"\n",
"There are two storage formats:\n",
"* `mv` (for \"multi-valued\"): Each logic value is stored as uint8\n",
"* `bp` (for \"bit-parallel\"): Groups of 8 logic values are stored as three uint8"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### `mv` Arrays\n",
"\n",
"Suppose we want to simulate the adder circuit with 2 inputs, 1 output and 1 flip-flop."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0:__fork__\"a\" >3 >9,\n",
" 1:__fork__\"b\" >4 >10,\n",
" 2:__fork__\"s\" <5 ,\n",
" 4:DFF\"cin\" <1 >0]"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"adder.s_nodes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can construct a set of vectors using the `mvarray` helper function.\n",
"\n",
"Each vector has 2 elements, one for each io_node and sequential element.\n",
"\n",
"This would be an exhaustive vector set (the output in `s_nodes` remains unassigned (\"-\")):"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0, 3, 0, 3, 0, 3, 0, 3],\n",
" [0, 0, 3, 3, 0, 0, 3, 3],\n",
" [2, 2, 2, 2, 2, 2, 2, 2],\n",
" [0, 0, 0, 0, 3, 3, 3, 3]], dtype=uint8)"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from kyupy.logic import mvarray\n",
"\n",
"inputs = mvarray('00-0', '10-0', '01-0', '11-0', '00-1', '10-1', '01-1', '11-1')\n",
"inputs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The numeric values in this array are defined in `kyupy.logic`.\n",
"\n",
"The **last** axis is always the number of vectors. It may be unintuitive at first, but it is more convenient for data-parallel simulations.\n",
"\n",
"The **second-to-last** axis corresponds to `s_nodes`. I.e., the first row is for input 'a', the second row for input 'b', and so on."
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(4, 8)"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"inputs.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Get a string representation of a vector set. Possible values are '0', '1', '-', 'X', 'R', 'F', 'P', and 'N'."
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"00-0\n",
"10-0\n",
"01-0\n",
"11-0\n",
"00-1\n",
"10-1\n",
"01-1\n",
"11-1\n"
]
}
],
"source": [
"from kyupy.logic import mv_str\n",
"\n",
"print(mv_str(inputs))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Load a stuck-at fault test pattern set and expected fault-free responses from a STIL file. It contains 1081 test vectors."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
"from kyupy import stil\n",
"\n",
"s = stil.load('../tests/b14.stuck.stil.gz')\n",
"stuck_tests = s.tests(b14)\n",
"stuck_responses = s.responses(b14)"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"306"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(b14.s_nodes)"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(306, 1081)"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stuck_tests.shape"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(306, 1081)"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stuck_responses.shape"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"00--------------------00101011101101111011100010101100----------------------------------00-11110011001100110001100110011000100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001001100110011001100111000\n",
"P0--------------------11011111011001100111010101011101----------------------------------00-10111011010110011101110010111010111011101100010000110101111111011010101001010101010101010101001010110101001010101010101010110100000111111111111111011010100100101010010010101101010101001010100111010001010010000011100\n",
"P0--------------------00001101000101011100111111111111----------------------------------00-10000001000000010100000000000000110110110111111010101000101100101110001111101001110110100000110101001000100101000101010101001000011110110111111111000001111000010000101100010000100010100100011111101010001101000100011\n",
"P0--------------------11011111111110101011001111101111----------------------------------01-10000000000000000000000000000000001010011100110011010111011100010001110110011100101000011010101011111111111111111111111111111111011000011101010110111110010101100101010011100010001000010101010011010111100010100110111\n",
"P0--------------------00011111111110101001011001011111----------------------------------01-10000000001001010000000000000000111101001010000110000000010011010001100000010110001101100011010111000011011011010001000000000011111111111101000101011111111110000100101010000001010101011100000101001011010101010110001\n"
]
}
],
"source": [
"print(mv_str(stuck_tests[:,:5]))"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--11001100110011001100--------------------------------0011001100110011001100110011001110--011110011001100110001100110011000100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001001100110011001100111000\n",
"--10000010010100010111--------------------------------0101010010101010110101001001010100--011111110011011111000111010101010111011101100010000110101111111011010101001010101010101010101001010110101001010101010101010110100000111111111111111011010100100101010010010101101010101001010101000111111111111111011101\n",
"--01000101100010101111--------------------------------1000100101000100001000110100001010--001000001111111101000000000000000110110110111111010101000101100101110001111101001110110100000110101001000100101000101010101001000011001110111111111000001111000010000101100010000100010100100011111000111110100111000010\n",
"--11001010001111010110--------------------------------1010101000010001000111001010100101--110000000000000000000000000000000001010011100110011010111011100010001110110011100101000011010101000000000000000000000000000000000011100011101010110111110010100100101010011100010001000010101010011010111100010100110000\n",
"--11010101010110100101--------------------------------0000111010101010000001010100100000--001000000001110101100000000000000111101001010000110000000010011010001100000010110001101100011010111000011011011010001000000000011111000000011000101011111111110000100101010000001010101011100001000110000001011000111000\n"
]
}
],
"source": [
"print(mv_str(stuck_responses[:,:5]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The order of values in the vectors correspond to the circuit's `s_nodes`.\n",
"The test data can be used directly in the simulators as they use the same ordering convention.\n",
"\n",
"`stuck_tests` has values for all primary inputs and scan flip-flops, `stuck_responses` contains the expected values for all primary outputs and scan flip-flops.\n",
"\n",
"Since this is a static test, only '0' and '1' are used with the exception of the clock input, which has a positive pulse 'P'.\n",
"\n",
"A transition fault test is a dynamic test that also contains 'R' for rising transition and 'F' for falling transition:"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [],
"source": [
"s = stil.load('../tests/b14.transition.stil.gz')\n",
"transition_tests = s.tests_loc(b14)\n",
"transition_responses = s.responses(b14)"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"XX--------------------XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX----------------------------------XX-11110011001100110001100110011000100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001001100110011001100111000\n",
"00--------------------RRRRRRFRRRRRRRRRRRFFRFRRRRRRRRRR----------------------------------00-00000001110100011111011010000000000000000011001001100101111110101110110001000100010100110111111101101000000111110011100010111000111R1111111111111111111111110001100100000110100000111010101110RFF00F000F0F00F00000FF01F\n",
"00--------------------11R111110R0RR0R1110R01R1R001FRRR----------------------------------0F-RR0R00000000RR11R0RRR000R0R000R0010100010001011000111001000010001010111010101010100000000100001011100100001100011110110100000010011000011111100010111100010010111110100011100100011010000010111F11F0F01RRR110F0R01R011R\n",
"00--------------------RRFRRFR100FR10R010F10FR1111F111R----------------------------------0R-F01R0F01F100R01FF1F10R101FR1RR1R01001110010101110110000000100101111101001110100010011111111110100010001000100110100001111110011001001011011010111111111111111F10110101001010001001000001011001R0RF0R0R1FRR0RFR1RRRR101R\n",
"00--------------------FFR1RRF11FFRRR1R0FR100FF1R0FRFFF----------------------------------01-111FRRFF0RRRRRF01FFFRFRFFRRRFFRR11110100111100111000011001000111111000100011010000010000011111011111111110110111100100110001001111111000111111111000011101111000010010101000000101010101110000R1R1RRF001R1R1R1R10FF001R\n"
]
}
],
"source": [
"print(mv_str(transition_tests[:,:5]))"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--11001100110011001100--------------------------------0011001100110011001100110011001110--011110011001100110001100110011000100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001001100110011001100111000\n",
"--00000000000000000000--------------------------------0111010101110000010110000010011010--011111111111110111111100101111111000000000011001001100101111110101110110001000100010100110111111101101000000111110011100010111000111111111111111111111111111100011001000001101000001110101011101111111111111111111110011\n",
"--11010001111110000110--------------------------------1101000001011000100111000101111110--000010111111100000100011101011100010100010001011000111001000010001010111010101010100000000100001011100100001100011110110100000010011100011111100010111100010010111110100011100100011010000010110011000011111100010111110\n",
"--11111101011011010010--------------------------------1001101000001001000101001010110110--000110001010010100101011010111111101101001011001110000000000001001111010011101000100111111111101000100010001001101000011111100110010110110110101111111111111110101101010010100010010000010110010010010110110101111111010\n",
"--00011111111100011111--------------------------------0000111010101010000001010100100010--000011011100000110111010110001100000100110000110011111001101111001110001000110100000100000111110111111111101101111001001100010011111001001111111110000111011110000100101010000001010101011100001100101100011010110110010\n"
]
}
],
"source": [
"print(mv_str(transition_responses[:,:5]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### `bp` Arrays\n",
"\n",
"The logic simulator uses bit-parallel storage of logic values, but our loaded test data uses one `uint8` per logic value.\n",
"\n",
"Use `mv_to_bp` to convert mv data to the bit-parallel storage layout.\n",
"Bit-parallel storage is more compact, but individual values cannot be easily accessed anymore."
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [],
"source": [
"from kyupy.logic import mv_to_bp, bp_to_mv\n",
"\n",
"stuck_tests_bp = mv_to_bp(stuck_tests)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(306, 3, 136)"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stuck_tests_bp.data.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Instead of 1081 bytes per s_node, bit-parallel storage only uses 3*136=408 bytes.\n",
"\n",
"The reverse operation is `bp_to_mv`. Note that the number of vectors may be rounded up to the next multiple of 8:"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(306, 1088)"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bp_to_mv(stuck_tests_bp).shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logic Simulation\n",
"\n",
"The following code performs a 8-valued logic simulation on all 1081 vectors for one clock cycle."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [],
"source": [
"from kyupy.logic_sim import LogicSim\n",
"\n",
"sim = LogicSim(b14, sims=stuck_tests.shape[-1]) # 1081 simulations in parallel\n",
"sim.s[0] = stuck_tests_bp\n",
"sim.s_to_c()\n",
"sim.c_prop()\n",
"sim.c_to_s()\n",
"sim_responses = bp_to_mv(sim.s[1])[...,:stuck_tests.shape[-1]] # trim from 1088 -> 1081"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--11001100110011001100--------------------------------0011001100110011001100110011001110--000110110110111010111011100010100100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001100110011001100110011001\n",
"--10000010010100010111--------------------------------0101010010101010110101001001010100--011111110011011111000111010101010111011101100010000110101111111011010101001010101010101010101001010110101001010101010101010110100000111111111111111011010100100101010010010101101010101001010101000111111111111111011101\n",
"--01000101100010101111--------------------------------1000100101000100001000110100001010--001000001111111101000000000000000110110110111111010101000101100101110001111101001110110100000110101001000100101000101010101001000011001110111111111000001111000010000101100010000100010100100011111000111110100111000010\n",
"--11001010001111010110--------------------------------1010101000010001000111001010100101--110000000000000000000000000000000001010011100110011010111011100010001110110011100101000011010101000000000000000000000000000000000011100011101010110111110010100100101010011100010001000010101010011010111100010100110000\n",
"--11010101010110100101--------------------------------0000111010101010000001010100100000--001000000001110101100000000000000111101001010000110000000010011010001100000010110001101100011010111000011011011010001000000000011111000000011000101011111111110000100101010000001010101011100001000110000001011000111000\n"
]
}
],
"source": [
"print(mv_str(sim_responses[:,:5]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Compare simulation results to expected fault-free responses loaded from STIL.\n",
"\n",
"The first test fails, because it is a flush test while simulation implicitly assumes a standard test with a capture clock.\n",
"\n",
"The remaining 1080 responses are identical."
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1080"
]
},
"execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.sum(np.min(sim_responses == stuck_responses, axis=0))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Same simulation for the transition-fault test set:"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [],
"source": [
"sim = LogicSim(b14, sims=transition_tests.shape[-1]) # 1392 simulations in parallel\n",
"sim.s[0] = mv_to_bp(transition_tests)\n",
"sim.s_to_c()\n",
"sim.c_prop()\n",
"sim.c_to_s()\n",
"sim_responses = bp_to_mv(sim.s[1])[...,:transition_tests.shape[-1]] # trim to 1392"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--11001100110011001100--------------------------------0011001100110011001100110011001110--0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001100110011001100110011001\n",
"--F00000F00F0F000F00FF--------------------------------01110101011100000101100000100110R0--0RRRRRRRNNNRNRPRNNNNNRFFRFRRRRRRR000000000011001001100101111110101110110001000100010100110111111101101000000111110011100010111000NNNNNNNNNNNNNNNNNNNNNNNNNNNNP0011001000001101000001110101011101RRRRRRRRRRRRRRRRRRRRP01R\n",
"--R10R0F011RRR10F0F11F--------------------------------1101000001011000100111000101111110--0FFPNPRRRRRRRFFFFFRFFFRRRFRFRRRFPPNPNPPPNPPPNPNNPPPNNNPPNPPPPNPPPNPNPNNNPNPNPNPNPNPPPPPPPPNPPPPNPNNNPPNPPPPNNPPPNNNNPNNPNPPPPPPNPPNNRPPPNNNNNNPPPNPNNNNPPPNPPNPNNNNNPNPPPNNNPPNPPPNNPNP0P00N0NNFPNNPPPPNNNNNNPPPNPNNRNNF\n",
"--RRRR1RFR0RRF1R0R0FR0--------------------------------10011010000010010001010010101101RF--FPPNNPPPNPNPPNPNPPNPNPNNPNPNNNNNNRFRRFNFPRFRNPFNNRFFPPPPPPPFPPNPFNNNNPNPPNNNPNPPPNPPNNNNNNNNNNPNPPPNPPPNPPPNPPNNPNPPPPNNNNNNPPNNPPNPRNPNNPNNPNPNNNNNNNNNNNNNNNPNPNNPNPNPPNPNPPPNPPNPPPPPNPNNPPNFPNPPNPNNPNNPNPNNNNNNNPNF\n",
"--FF01R1R1R1R100FRR1R1--------------------------------00001110101010100000010101001000R0--0FFFRNFRRRFFFFFRRFRRRFRFRRFFFRRFFFFFNPFRRFFFFRRFFNRRRRFFRRFRRRNFFNNNPPPNPPPNNPNPPPPPNPPPPPNNNNNPNNNNNNNNNNPNNPNNNNPPNPPNNPPPNPPNNNNNFFRPPNNNNNNNNNPPPPNNNPNNNNPPPPNPPNPN0NP0PPPPN0N0NPN0NNNPPPPNNFFNFRRPFFNNFNFNNPRRPPNF\n"
]
}
],
"source": [
"print(mv_str(sim_responses[:,:5]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The simulator responses contain 'R' for rising transition, 'F' for falling transition, 'P' for possible positive pulse(s) (010) and 'N' for possible negative pulse(s) (101).\n",
"\n",
"We need to map each of these cases to the final logic values before we can compare:"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--11001100110011001100--------------------------------0011001100110011001100110011001110--0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110110011001100110011001100110011001100110011001100110011001\n",
"--00000000000000000000--------------------------------0111010101110000010110000010011010--011111111111110111111100101111111000000000011001001100101111110101110110001000100010100110111111101101000000111110011100010111000111111111111111111111111111100011001000001101000001110101011101111111111111111111110011\n",
"--11010001111110000110--------------------------------1101000001011000100111000101111110--000010111111100000100011101011100010100010001011000111001000010001010111010101010100000000100001011100100001100011110110100000010011100011111100010111100010010111110100011100100011010000010110011000011111100010111110\n",
"--11111101011011010010--------------------------------1001101000001001000101001010110110--000110001010010100101011010111111101101001011001110000000000001001111010011101000100111111111101000100010001001101000011111100110010110110110101111111111111110101101010010100010010000010110010010010110110101111111010\n",
"--00011111111100011111--------------------------------0000111010101010000001010100100010--000011011100000110111010110001100000100110000110011111001101111001110001000110100000100000111110111111111101101111001001100010011111001001111111110000111011110000100101010000001010101011100001100101100011010110110010\n"
]
}
],
"source": [
"sim_responses_final = np.choose(sim_responses, mvarray('0X-10101')) # '0X-1PRFN'\n",
"print(mv_str(sim_responses_final[:,:5]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Again, first test is a flush test, so we expect 1391 matches."
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1391"
]
},
"execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.sum(np.min(sim_responses_final == transition_responses, axis=0))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Working With Delay Information and Timing Simulation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Delay data for gates and interconnect can be loaded from SDF files. In kyupy's timing simulators, delays are associated with the lines between nodes, not with the nodes themselves. Each line in the circuit has a rising delay, a falling delay, a negative pulse threshold, and a positive pulse threshold. "
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [],
"source": [
"from kyupy import sdf\n",
"\n",
"df = sdf.load('../tests/b14.sdf.gz')\n",
"lt = df.annotation(b14, dataset=0, interconnect=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The returned delay information is an `ndarray` with a set of delay values for each line in the circuit."
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(46891, 2, 2)"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lt.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Number of non-0 values loaded:"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"120628"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(lt != 0).sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The available timing simulators are `WaveSim` and `WaveSimCuda`.\n",
"They work similarly to `LogicSim` in that they evaluate all cells in topological order.\n",
"Instead of propagating a logic value, however, they propagate waveforms.\n",
"\n",
"`WaveSim` uses the numba just-in-time compiler for acceleration on CPU.\n",
"It falls back to pure python if numba is not available. `WaveSimCuda` uses numba for GPU acceleration.\n",
"If no CUDA card is available, it will fall back to pure python (not jit-compiled for CPU!).\n",
"Pure python is too slow for most purposes.\n",
"\n",
"Both simulators operate data-parallel.\n",
"The following instanciates a new engine for 32 independent timing simulations and each signal line in the circuit can carry at most 16 transitions. All simulators share the same circuit and the same line delay specification."
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [],
"source": [
"from kyupy.wave_sim import WaveSimCuda, TMAX\n",
"import numpy as np\n",
"\n",
"wsim = WaveSimCuda(b14, lt, sims=32, c_caps=16)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These are various memories allocated, with waveforms usually being the largest. "
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Waveforms : 93908.5 kiB\n",
"State Allocation Table : 1113.4 kiB\n",
"Circuit Timing : 1484.5 kiB\n",
"Circuit Netlist : 1099.0 kiB\n",
"Sequential State : 420.8 kiB\n"
]
}
],
"source": [
"def print_mem(name, arr):\n",
" print(f'{name}: {arr.nbytes / 1024:.1f} kiB')\n",
" \n",
"print_mem('Waveforms ', wsim.c)\n",
"print_mem('State Allocation Table ', wsim.vat)\n",
"print_mem('Circuit Timing ', wsim.timing)\n",
"print_mem('Circuit Netlist ', wsim.ops)\n",
"print_mem('Sequential State ', wsim.s)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is a typical simulation loop where the number of patterns is larger than the number of simulators available.\n",
"We simulate `trans_tests_bp`.\n",
"The timing simulator accepts 8-valued `BPArray`s, but it will return response (capture) data in a different format."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"sims = 128 # trans_tests.shape[-1] # Feel free to simulate all tests if CUDA is set up correctly.\n",
"\n",
"cdata = np.zeros((len(wsim.interface), sims, 7)) # space to store all capture data\n",
"\n",
"for offset in range(0, sims, wsim.sims):\n",
" wsim.assign(trans_tests_bp, offset=offset)\n",
" wsim.propagate(sims=sims-offset)\n",
" wsim.capture(time=2.5, cdata=cdata, offset=offset) # capture at time 2.5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The capture data contains for each PI, PO, and scan flip-flop (axis 0), and each test (axis 1) seven values:\n",
"\n",
"0. Probability of capturing a 1 at the given capture time (same as next value, if no standard deviation given).\n",
"1. A capture value decided by random sampling according to above probability.\n",
"2. The final value (assume a very late capture time).\n",
"3. True, if there was a premature capture (capture error), i.e. final value is different from captured value.\n",
"4. Earliest arrival time. The time at which the output transitioned from its initial value.\n",
"5. Latest stabilization time. The time at which the output transitioned to its final value.\n",
"6. Overflow indicator. If non-zero, some signals in the input cone of this output had more transitions than specified in `wavecaps`. Some transitions have been discarded, the final values in the waveforms are still valid."
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(306, 128, 7)"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cdata.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For validating against known logic values, take `cdata[...,1]`."
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mismatch for test pattern 0\n",
"127 of 128 responses matched with simulator\n"
]
}
],
"source": [
"matches = 0\n",
"\n",
"for i in range(cdata.shape[1]):\n",
" response = ''.join('1' if x > 0.5 else '0' for x in cdata[..., i, 1])\n",
" if trans_responses[i].replace('-','0') == response:\n",
" matches += 1\n",
" else:\n",
" print(f'mismatch for test pattern {i}')\n",
"print(f'{matches} of {cdata.shape[1]} responses matched with simulator')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The circuit delay is the maximum among all latest stabilization times:"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2.17240047454834"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cdata[...,5].max()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Check for overflows. If too many of them occur, increase `wavecaps` during engine instanciation:"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2.0"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cdata[...,6].sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Check for capture failures:"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.0"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cdata[...,3].sum()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"vscode": {
"interpreter": {
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}