mirror of https://github.com/VLSIDA/OpenRAM.git
Compare commits
504 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
ea15a81443 | |
|
|
9492349d7a | |
|
|
01686a2005 | |
|
|
e63f70da5e | |
|
|
8104a42f0e | |
|
|
bc1cc36ade | |
|
|
3184e1d0e4 | |
|
|
7ec407314a | |
|
|
1f5fe62456 | |
|
|
3f1f58065d | |
|
|
0937f86761 | |
|
|
85e242fa27 | |
|
|
b6a6f12642 | |
|
|
306da8a895 | |
|
|
0cf60a6a18 | |
|
|
55e5c425e9 | |
|
|
14c219d9f1 | |
|
|
855139bc4e | |
|
|
0423f10926 | |
|
|
156eb4f0f7 | |
|
|
18b19b8d9d | |
|
|
5081cf2383 | |
|
|
84020f13c6 | |
|
|
0861ea5632 | |
|
|
daa6286a3a | |
|
|
e7dbf7443e | |
|
|
0a1de57cae | |
|
|
fc37bf6859 | |
|
|
8032fa75a4 | |
|
|
05884cf6a7 | |
|
|
9d6052b86c | |
|
|
caa9b2a2d8 | |
|
|
e35f060d32 | |
|
|
efd43c3191 | |
|
|
7531e38cad | |
|
|
39a66fcb87 | |
|
|
6cfb22959c | |
|
|
02810a0740 | |
|
|
e73ff401fb | |
|
|
6bd437cfa8 | |
|
|
d59a60eaf3 | |
|
|
b9570b8ddf | |
|
|
6e2dadeff5 | |
|
|
fc1a9a9a2b | |
|
|
ce1861f342 | |
|
|
d161cc55a5 | |
|
|
a45e16bff5 | |
|
|
26068fd2e1 | |
|
|
b453aa23c2 | |
|
|
5378a308c1 | |
|
|
bd9ebc3300 | |
|
|
561e0c228c | |
|
|
4b4153bdea | |
|
|
98a4210b06 | |
|
|
042bfcabea | |
|
|
b65ebc6160 | |
|
|
1f35855c6d | |
|
|
9b99e6c124 | |
|
|
ddba3b3718 | |
|
|
5c22e382b5 | |
|
|
4b3af38727 | |
|
|
565e3f6814 | |
|
|
3271c5e73c | |
|
|
75f7a5847f | |
|
|
b279791762 | |
|
|
a544abebf7 | |
|
|
20d0df2947 | |
|
|
cb8567c66f | |
|
|
d940c0e03d | |
|
|
23611f8fac | |
|
|
e3b51360f3 | |
|
|
ea703d124f | |
|
|
07fa78e00c | |
|
|
dba75fc57c | |
|
|
9ac94d1744 | |
|
|
0aa9c47f89 | |
|
|
8ac30f4ef5 | |
|
|
416140d04a | |
|
|
8c56478df3 | |
|
|
13459cb6dd | |
|
|
88782b0a58 | |
|
|
4bb586c949 | |
|
|
d6cb15c82d | |
|
|
0040efb86f | |
|
|
b0a0226e87 | |
|
|
1255a81487 | |
|
|
c09a981734 | |
|
|
698020301c | |
|
|
7ce11eba52 | |
|
|
b9fd172e44 | |
|
|
de7a248ff0 | |
|
|
81b62ab13b | |
|
|
8a5b0b4898 | |
|
|
a904874978 | |
|
|
d18a4f8c7c | |
|
|
a18d62c430 | |
|
|
06058e1b87 | |
|
|
decfd7ff4f | |
|
|
788d7e5474 | |
|
|
3113798b13 | |
|
|
fe379297be | |
|
|
6a6ac026db | |
|
|
a5412902c6 | |
|
|
1b13d4369e | |
|
|
bf49ea744e | |
|
|
c47ec37473 | |
|
|
0a3889a2a6 | |
|
|
5b282df667 | |
|
|
c4a14b9354 | |
|
|
ad2468cc26 | |
|
|
2e55385cd6 | |
|
|
3b6cc2f3a8 | |
|
|
1a8c27c549 | |
|
|
b3e1a163d0 | |
|
|
9c66473cc9 | |
|
|
995cc4f60f | |
|
|
099869d4c9 | |
|
|
3f2d61a0fa | |
|
|
1a25fcf9a5 | |
|
|
abb12bd785 | |
|
|
775922774a | |
|
|
d9004f6de6 | |
|
|
8a60684e51 | |
|
|
a9e63efad7 | |
|
|
b281771753 | |
|
|
56bee27ee3 | |
|
|
b525ba60a0 | |
|
|
e12ab68362 | |
|
|
fa5de05be3 | |
|
|
53cc99f5c1 | |
|
|
9df3c2ac59 | |
|
|
141a4e3380 | |
|
|
0ac2922573 | |
|
|
ca5ca0c7a8 | |
|
|
3eaf973d89 | |
|
|
230454d567 | |
|
|
3f08c848d7 | |
|
|
cc8161afc1 | |
|
|
ea02aae40f | |
|
|
9ac82060b9 | |
|
|
ad05fde099 | |
|
|
fa1b2fc96e | |
|
|
f8b2c1e9b9 | |
|
|
8fff4e2635 | |
|
|
54fc34392d | |
|
|
e1d0902680 | |
|
|
ba8e80d205 | |
|
|
87eca6b7db | |
|
|
08dad81214 | |
|
|
5b0f97860a | |
|
|
937585d23c | |
|
|
877f20e071 | |
|
|
887a66553b | |
|
|
dd152da5c2 | |
|
|
42257fb7f8 | |
|
|
993b47be4c | |
|
|
93a6549539 | |
|
|
6c70396a05 | |
|
|
da24c52c52 | |
|
|
db2a276077 | |
|
|
7be6f2783b | |
|
|
4b2659a5e2 | |
|
|
5cf774b53e | |
|
|
e5bc7b4e95 | |
|
|
d487f788e3 | |
|
|
821c763a1e | |
|
|
3b0997e7cf | |
|
|
4c73d3aa7c | |
|
|
091d0f8775 | |
|
|
8522e0108c | |
|
|
4d835e98b7 | |
|
|
53505e2ed2 | |
|
|
d609ef9243 | |
|
|
62a04ce874 | |
|
|
54ce1377c5 | |
|
|
b1f4f0887e | |
|
|
6cda5415a4 | |
|
|
1de6b9a0f6 | |
|
|
ed404a3ad2 | |
|
|
47185f6085 | |
|
|
587d44e536 | |
|
|
2b15289daf | |
|
|
fa74a45d8c | |
|
|
5de7b9cda7 | |
|
|
947e94323d | |
|
|
54f2e73214 | |
|
|
7119f9a131 | |
|
|
542e1a5e03 | |
|
|
a90fd36f57 | |
|
|
7a7ddebcca | |
|
|
1c274afa46 | |
|
|
06b8f3b2be | |
|
|
48b556c43a | |
|
|
ecd486e050 | |
|
|
f86d1dbdf9 | |
|
|
8354de654f | |
|
|
3e3265c416 | |
|
|
44b2e4589c | |
|
|
7ee1dcef54 | |
|
|
93182d525d | |
|
|
5b1544ad45 | |
|
|
7aceadf9ba | |
|
|
78e5b60380 | |
|
|
f800b50813 | |
|
|
f246e5a521 | |
|
|
5ce193c2dd | |
|
|
6751442d35 | |
|
|
2051f54f70 | |
|
|
0b2196a3e4 | |
|
|
6e3e964c12 | |
|
|
f41537b508 | |
|
|
b30dfa03e7 | |
|
|
2ced895b32 | |
|
|
c67fdd8bd8 | |
|
|
d53353b5be | |
|
|
9fa25690ce | |
|
|
2b9e70d318 | |
|
|
e8c3cf0a94 | |
|
|
44e21dacba | |
|
|
5dbc22091c | |
|
|
bfabe64f33 | |
|
|
09aa395174 | |
|
|
5907cbb3e2 | |
|
|
54c2710aea | |
|
|
3ef61a298f | |
|
|
aa71785bd5 | |
|
|
699f7b311e | |
|
|
478c76c1ca | |
|
|
148d80531e | |
|
|
53d00f5b34 | |
|
|
5ef964d01f | |
|
|
7bd312faff | |
|
|
8a441bc68b | |
|
|
b5983fbfd6 | |
|
|
b1b6886bac | |
|
|
45b88889e4 | |
|
|
6e051e7f06 | |
|
|
d0339a90e6 | |
|
|
e15feb2361 | |
|
|
7581df2255 | |
|
|
38110a55e1 | |
|
|
e501e0ef4f | |
|
|
983cf13ccf | |
|
|
c8c43f75d9 | |
|
|
146efc5070 | |
|
|
094e71764a | |
|
|
71e4a5ab6c | |
|
|
813a67fea9 | |
|
|
833d4c12a6 | |
|
|
37b7de4653 | |
|
|
89d8441108 | |
|
|
042a3ed14f | |
|
|
ed0c93ba55 | |
|
|
eddc9af45b | |
|
|
4e649aad6b | |
|
|
513c7e9f71 | |
|
|
0ad619f04c | |
|
|
6b0b4c2def | |
|
|
4a61874888 | |
|
|
7220e0a483 | |
|
|
b91c628acf | |
|
|
468c972acb | |
|
|
071670bef0 | |
|
|
d65ccfcc95 | |
|
|
b4a9784835 | |
|
|
5235cf9667 | |
|
|
bb35ac2f90 | |
|
|
0938e7ec9a | |
|
|
78be525ea0 | |
|
|
e1865083d7 | |
|
|
5bf629f3e5 | |
|
|
91694fdae3 | |
|
|
3620d56790 | |
|
|
14f8008c4f | |
|
|
28ea93bd0a | |
|
|
006eacd6d0 | |
|
|
3cd3a63419 | |
|
|
3ada5347eb | |
|
|
92c8770472 | |
|
|
8992c0fb68 | |
|
|
a47bc7ebee | |
|
|
dbc9de6c9a | |
|
|
542df33878 | |
|
|
9a36cce7ae | |
|
|
b9e61f346a | |
|
|
a3284e8b47 | |
|
|
bf516a927d | |
|
|
fee90283b9 | |
|
|
692acd2066 | |
|
|
96a1d400fa | |
|
|
266bcd9cf2 | |
|
|
854bff9dce | |
|
|
7048a072e2 | |
|
|
44ed72b50d | |
|
|
ce622952ef | |
|
|
a51b71d460 | |
|
|
539dfc979a | |
|
|
973b5512f0 | |
|
|
dcf95460d0 | |
|
|
9256ae8c00 | |
|
|
afd3b782b9 | |
|
|
a48842ff72 | |
|
|
b9492051b6 | |
|
|
a70dcc5c85 | |
|
|
9fdf8a8341 | |
|
|
157935c915 | |
|
|
15b4e4dbe8 | |
|
|
5fef78dbfa | |
|
|
2f5d3b6faf | |
|
|
df827fbd3d | |
|
|
5b10f06be6 | |
|
|
0b5039cc89 | |
|
|
48a148003a | |
|
|
23232fd376 | |
|
|
8f1af0ebb7 | |
|
|
021da25cd6 | |
|
|
4fe5aa49e4 | |
|
|
e3d8ad13b2 | |
|
|
2799c106bd | |
|
|
136d4564a2 | |
|
|
9f75e68a92 | |
|
|
6079152092 | |
|
|
e1e24f6d06 | |
|
|
533c1c9472 | |
|
|
abd18ab832 | |
|
|
054b7cd47d | |
|
|
f16a40af02 | |
|
|
15c5e57d77 | |
|
|
e13cc76ac3 | |
|
|
6841de4a50 | |
|
|
33f1b924a4 | |
|
|
648a631a28 | |
|
|
49efff3384 | |
|
|
5cdecd781b | |
|
|
217b0981a2 | |
|
|
b9123571f4 | |
|
|
2709f61317 | |
|
|
79e5c1ad86 | |
|
|
dbb8bb85cb | |
|
|
cd339ebbd0 | |
|
|
909ac6ce68 | |
|
|
bb8f3f7eb8 | |
|
|
f5bc031d83 | |
|
|
123149503b | |
|
|
744ba0e892 | |
|
|
a5a0cffe65 | |
|
|
afe37e5915 | |
|
|
b2b7e1fa4d | |
|
|
3f94e22860 | |
|
|
07411892c1 | |
|
|
44ca70bd16 | |
|
|
bd7b2c22c7 | |
|
|
7ed99278bd | |
|
|
6af9c556a9 | |
|
|
773ea1af0d | |
|
|
dae275c508 | |
|
|
027b93ab83 | |
|
|
0ff1d1a23d | |
|
|
8d0c46d069 | |
|
|
eea748ff3e | |
|
|
670b40642b | |
|
|
dff94a032e | |
|
|
5b701d828e | |
|
|
3c7f35d295 | |
|
|
efbb658784 | |
|
|
ae6d271602 | |
|
|
d00ba73bc9 | |
|
|
7ed7ff4e9a | |
|
|
b2bcbddd01 | |
|
|
83b25138d0 | |
|
|
9181f6a218 | |
|
|
299512eba2 | |
|
|
e20f28580f | |
|
|
710f0fbae5 | |
|
|
41344a980b | |
|
|
7abaf0463e | |
|
|
249d1b9c1d | |
|
|
c447ec49eb | |
|
|
613146520e | |
|
|
f7f61fee27 | |
|
|
6eb0ecd82b | |
|
|
8650315179 | |
|
|
bce71af0ad | |
|
|
3496ac8f5a | |
|
|
6bdcdb8f37 | |
|
|
3b69cafde7 | |
|
|
4436c61a39 | |
|
|
c8a06a1317 | |
|
|
7a62ec0030 | |
|
|
9002a8ac70 | |
|
|
4ec2dd2d1f | |
|
|
b70f919a2b | |
|
|
39104fa9d8 | |
|
|
0d60c76e3b | |
|
|
da86d50766 | |
|
|
5695cd69c6 | |
|
|
8de3be8529 | |
|
|
149abe1dbd | |
|
|
817dc8a063 | |
|
|
a7cbf254be | |
|
|
bcb0f379fe | |
|
|
7fdc5cc782 | |
|
|
d27d77bcf8 | |
|
|
8a67626e55 | |
|
|
db85e8ecd6 | |
|
|
c6485277f3 | |
|
|
b9f16ea490 | |
|
|
816eff711d | |
|
|
6603220258 | |
|
|
2b79646b8f | |
|
|
c6440dc16d | |
|
|
2d8d90952e | |
|
|
214f55f8d7 | |
|
|
b1e4c83373 | |
|
|
b9dbad4750 | |
|
|
fcfb9391f6 | |
|
|
05ab45f39b | |
|
|
d3753556c1 | |
|
|
eceb35f205 | |
|
|
56879bf48b | |
|
|
f0a4665953 | |
|
|
efd6da5300 | |
|
|
9fba946f18 | |
|
|
f602c6b263 | |
|
|
bd6621cb88 | |
|
|
3f941d2fff | |
|
|
f8c80999bd | |
|
|
c7975e3274 | |
|
|
ae107b635f | |
|
|
2101067e4a | |
|
|
219b29a833 | |
|
|
8872a3e312 | |
|
|
8d1d3c0e90 | |
|
|
e621890f78 | |
|
|
3e528a3e75 | |
|
|
9e29391992 | |
|
|
2adab1ea1a | |
|
|
ebe4393d66 | |
|
|
34ee709c69 | |
|
|
2bbd293bf2 | |
|
|
8793dda40a | |
|
|
e2a52ec0f3 | |
|
|
28128157c0 | |
|
|
f01e73328d | |
|
|
b82213caff | |
|
|
480862c765 | |
|
|
75efc476f7 | |
|
|
5fa0689c02 | |
|
|
08ac1c176a | |
|
|
12c58b0457 | |
|
|
73021be8eb | |
|
|
2611468dd7 | |
|
|
9182ad7c61 | |
|
|
7f52e63aca | |
|
|
231dca5b51 | |
|
|
74bf3770d9 | |
|
|
7567db6fe9 | |
|
|
96046096b4 | |
|
|
fd7a7c2564 | |
|
|
1e1ec54275 | |
|
|
3526a57864 | |
|
|
1d6bd78612 | |
|
|
d7b1368115 | |
|
|
63ea1588c1 | |
|
|
0a3c1dd9b8 | |
|
|
7b4af87fda | |
|
|
5edb511dab | |
|
|
71f241f660 | |
|
|
67c1560df0 | |
|
|
fede082b80 | |
|
|
30b9c2fc25 | |
|
|
606260dd68 | |
|
|
b9b57ab6b3 | |
|
|
06254fae72 | |
|
|
1d0741baa4 | |
|
|
ef2c9fe296 | |
|
|
7d4b718344 | |
|
|
45239ca2a9 | |
|
|
c4138c9f9b | |
|
|
2b72fbee4e | |
|
|
11ea82e782 | |
|
|
78013d32b7 | |
|
|
62a65f8053 | |
|
|
66502fc5dc | |
|
|
b05a721fb5 | |
|
|
e1fcd90b59 | |
|
|
471f68fab2 | |
|
|
9aafada1ab | |
|
|
45df34c774 | |
|
|
9ec87c4d52 | |
|
|
06cd2620f5 | |
|
|
4514927a7b | |
|
|
8bbe39f5d1 | |
|
|
ef710eae7a | |
|
|
043e93e75e | |
|
|
b216552c3f | |
|
|
a89cd4deee | |
|
|
f9bec36da5 | |
|
|
353ff7859c | |
|
|
1acc6be3ce | |
|
|
910980328c | |
|
|
d47b5612d5 | |
|
|
a87b2e9446 | |
|
|
dd2effd28d |
|
|
@ -12,6 +12,9 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip3 install -r requirements.txt
|
||||
- name: Library build
|
||||
run: |
|
||||
rm -rf ~/.local/lib/python3.8/site-packages/openram*
|
||||
|
|
@ -24,8 +27,9 @@ jobs:
|
|||
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
||||
export OPENRAM_TECH="${{ github.workspace }}/technology"
|
||||
export PDK_ROOT="${{ github.workspace }}/pdk"
|
||||
make pdk
|
||||
make install
|
||||
# Add make targets to install PDKs of all technologies that need it
|
||||
make sky130-pdk
|
||||
make sky130-install
|
||||
- name: Regress
|
||||
run: |
|
||||
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
||||
|
|
@ -43,7 +47,7 @@ jobs:
|
|||
make -k -j 48
|
||||
- name: Archive
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Regress Archives
|
||||
path: ${{ github.workspace }}/compiler/tests/results/*
|
||||
|
|
|
|||
|
|
@ -13,13 +13,20 @@ outputs
|
|||
technology/freepdk45/ncsu_basekit
|
||||
technology/sky130/*_lib
|
||||
technology/sky130/tech/.magicrc
|
||||
technology/gf180mcu/*_lib
|
||||
.idea
|
||||
compiler/tests/results/
|
||||
open_pdks/
|
||||
dist/
|
||||
openram.egg-info/
|
||||
miniconda/
|
||||
sky130A/
|
||||
sky130B/
|
||||
sky130A
|
||||
sky130B
|
||||
gf180mcuA
|
||||
gf180mcuB
|
||||
gf180mcuC
|
||||
gf180mcuD
|
||||
skywater-pdk/
|
||||
sky130_fd_bd_sram/
|
||||
docker/openram-ubuntu.log
|
||||
ciel/
|
||||
|
|
|
|||
91
Makefile
91
Makefile
|
|
@ -3,6 +3,9 @@ include $(TOP_DIR)/openram.mk
|
|||
|
||||
.DEFAULT_GOAL := install
|
||||
|
||||
# Set the shell here
|
||||
SHELL := /bin/bash
|
||||
|
||||
# Skywater PDK SRAM library
|
||||
SRAM_LIB_DIR ?= $(PDK_ROOT)/sky130_fd_bd_sram
|
||||
# Use this for release
|
||||
|
|
@ -10,20 +13,27 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git
|
|||
# Use this for development
|
||||
#SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git
|
||||
#SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
|
||||
SRAM_LIB_GIT_COMMIT ?= a83b6468c48434d927b90058b22047843c58027b
|
||||
SRAM_LIB_GIT_COMMIT ?= dd64256961317205343a3fd446908b42bafba388
|
||||
|
||||
# Open PDKs
|
||||
OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks
|
||||
OPEN_PDKS_GIT_REPO ?= https://github.com/RTimothyEdwards/open_pdks.git
|
||||
OPEN_PDKS_GIT_COMMIT ?= 1.0.311
|
||||
#OPEN_PDKS_GIT_COMMIT ?= 7ea416610339d3c29af9d0d748ceadd3fd368608
|
||||
SKY130_PDK ?= $(PDK_ROOT)/sky130A
|
||||
GF180_PDK ?= $(PDK_ROOT)/gf180mcuD
|
||||
|
||||
# Ciel SKY130 PDK
|
||||
SKY130_CIEL = e8294524e5f67c533c5d0c3afa0bcc5b2a5fa066 # 2022.07.29
|
||||
|
||||
# Ciel GF180 PDK
|
||||
GF180_CIEL = cd1748bb197f9b7af62a54507de6624e30363943 # 2023.12.04
|
||||
|
||||
# Skywater PDK
|
||||
SKY130_PDKS_DIR ?= $(PDK_ROOT)/skywater-pdk
|
||||
SKY130_PDKS_GIT_REPO ?= https://github.com/google/skywater-pdk.git
|
||||
SKY130_PDKS_GIT_COMMIT ?= f70d8ca46961ff92719d8870a18a076370b85f6c
|
||||
|
||||
# GF180 PDK
|
||||
GF180_PDKS_DIR ?= $(PDK_ROOT)/gf180mcu-pdk
|
||||
GF180_PDKS_GIT_REPO ?= https://github.com/google/gf180mcu-pdk.git
|
||||
GF180_PDKS_GIT_COMMIT ?= main
|
||||
|
||||
# Create lists of all the files to copy/link
|
||||
GDS_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.gds))
|
||||
GDS_FILES := $(GDS_FILES) $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/dlxtn/sky130_fd_sc_hd__dlxtn_1.gds
|
||||
|
|
@ -44,12 +54,11 @@ MAGICRC_FILE := $(SKY130_PDK)/libs.tech/magic/sky130A.magicrc
|
|||
|
||||
ALL_FILES := $(ALL_SPICE_FILES) $(GDS_FILES) $(MAG_FILES) $(MAGLEF_FILES)
|
||||
|
||||
|
||||
INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib klayout_lvs_lib maglef_lib
|
||||
INSTALL_BASE := $(OPENRAM_HOME)/../technology/sky130
|
||||
INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS))
|
||||
|
||||
# If conda is installed, we will use Magic from there
|
||||
# If conda is installed, we will use ciel from there
|
||||
CONDA_DIR := $(wildcard $(TOP_DIR)/miniconda)
|
||||
|
||||
check-pdk-root:
|
||||
|
|
@ -64,53 +73,55 @@ $(SKY130_PDKS_DIR): check-pdk-root
|
|||
@git -C $(SKY130_PDKS_DIR) checkout $(SKY130_PDKS_GIT_COMMIT) && \
|
||||
git -C $(SKY130_PDKS_DIR) submodule update --init libraries/sky130_fd_pr/latest libraries/sky130_fd_sc_hd/latest
|
||||
|
||||
$(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR)
|
||||
@echo "Cloning open_pdks..."
|
||||
@[ -d $(OPEN_PDKS_DIR) ] || \
|
||||
git clone $(OPEN_PDKS_GIT_REPO) $(OPEN_PDKS_DIR)
|
||||
@git -C $(OPEN_PDKS_DIR) checkout $(OPEN_PDKS_GIT_COMMIT)
|
||||
|
||||
$(SKY130_PDK): $(OPEN_PDKS_DIR) $(SKY130_PDKS_DIR)
|
||||
@echo "Installing open_pdks..."
|
||||
ifeq ($(CONDA_DIR),"")
|
||||
@cd $(PDK_ROOT)/open_pdks && \
|
||||
./configure --enable-sky130-pdk=$(PDK_ROOT)/skywater-pdk/libraries --with-sky130-local-path=$(PDK_ROOT) && \
|
||||
cd sky130 && \
|
||||
make veryclean && \
|
||||
make && \
|
||||
make SHARED_PDKS_PATH=$(PDK_ROOT) install
|
||||
else
|
||||
@source $(TOP_DIR)/miniconda/bin/activate && \
|
||||
cd $(PDK_ROOT)/open_pdks && \
|
||||
./configure --enable-sky130-pdk=$(PDK_ROOT)/skywater-pdk/libraries --with-sky130-local-path=$(PDK_ROOT) && \
|
||||
cd sky130 && \
|
||||
make veryclean && \
|
||||
make && \
|
||||
make SHARED_PDKS_PATH=$(PDK_ROOT) install && \
|
||||
conda deactivate
|
||||
endif
|
||||
$(GF180_PDKS_DIR): check-pdk-root
|
||||
@echo "Cloning gf PDK..."
|
||||
@[ -d $(PDK_ROOT)/gf180mcu-pdk ] || \
|
||||
git clone https://github.com/google/gf180mcu-pdk.git $(PDK_ROOT)/gf180mcu-pdk
|
||||
@cd $(GF180_PDKS_DIR) && \
|
||||
git checkout main && git pull && \
|
||||
git checkout -qf $(GF180_PDKS_GIT_COMMIT) && \
|
||||
git submodule update --init libraries/gf180mcu_fd_pr/latest libraries/gf180mcu_fd_sc_mcu7t5v0/latest libraries/gf180mcu_fd_sc_mcu9t5v0/latest
|
||||
|
||||
$(SRAM_LIB_DIR): check-pdk-root
|
||||
@echo "Cloning SRAM library..."
|
||||
@[ -d $(SRAM_LIB_DIR) ] || \
|
||||
git clone $(SRAM_LIB_GIT_REPO) $(SRAM_LIB_DIR)
|
||||
@git -C $(SRAM_LIB_DIR) fetch
|
||||
@git -C $(SRAM_LIB_DIR) checkout $(SRAM_LIB_GIT_COMMIT)
|
||||
|
||||
install: $(SRAM_LIB_DIR)
|
||||
sky130-install: $(SRAM_LIB_DIR)
|
||||
@[ -d $(PDK_ROOT)/sky130A ] || \
|
||||
(echo "Warning: $(PDK_ROOT)/sky130A not found!! Run make pdk first." && false)
|
||||
(echo "Warning: $(PDK_ROOT)/sky130A not found!! Run make sky130-pdk first." && false)
|
||||
@[ -d $(PDK_ROOT)/skywater-pdk ] || \
|
||||
(echo "Warning: $(PDK_ROOT)/skywater-pdk not found!! Run make pdk first." && false)
|
||||
(echo "Warning: $(PDK_ROOT)/skywater-pdk not found!! Run make sky130-pdk first." && false)
|
||||
@echo "Installing sky130 SRAM PDK..."
|
||||
@echo "PDK_ROOT='$(PDK_ROOT)'"
|
||||
@echo "SRAM_LIB_DIR='$(SRAM_LIB_DIR)'"
|
||||
@echo "SKY130_PDK='$(SKY130_PDK)'"
|
||||
@make $(INSTALL_DIRS)
|
||||
.PHONY: install
|
||||
.PHONY: sky130-install
|
||||
|
||||
pdk: $(SKY130_PDK)
|
||||
@true
|
||||
.PHONY: pdk
|
||||
sky130-pdk: $(SKY130_PDKS_DIR)
|
||||
@echo "Installing SKY130 via ciel..."
|
||||
ifeq ($(CONDA_DIR),)
|
||||
ciel enable --pdk sky130 $(SKY130_CIEL)
|
||||
else
|
||||
source $(TOP_DIR)/miniconda/bin/activate && \
|
||||
ciel enable --pdk sky130 $(SKY130_CIEL) && \
|
||||
conda deactivate
|
||||
endif
|
||||
.PHONY: sky130-pdk
|
||||
|
||||
gf180mcu-pdk:
|
||||
@echo "Installing GF180 via ciel..."
|
||||
ifeq ($(CONDA_DIR),)
|
||||
ciel enable --pdk gf180mcu $(GF180_CIEL)
|
||||
else
|
||||
source $(TOP_DIR)/miniconda/bin/activate && \
|
||||
ciel enable --pdk gf180mcu $(GF180_CIEL) && \
|
||||
conda deactivate
|
||||
endif
|
||||
.PHONY: gf180mcu-pdk
|
||||
|
||||
$(INSTALL_BASE)/gds_lib: $(GDS_FILES)
|
||||
@echo
|
||||
|
|
|
|||
29
README.md
29
README.md
|
|
@ -42,10 +42,6 @@ updating.
|
|||
# Further Help
|
||||
|
||||
+ [Documentation][documentation]
|
||||
+ [OpenRAM Slack Workspace][Slack]
|
||||
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
||||
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
|
||||
|
||||
|
||||
|
||||
# License
|
||||
|
|
@ -57,27 +53,27 @@ OpenRAM is licensed under the [BSD 3-Clause License](./LICENSE).
|
|||
# Publications
|
||||
|
||||
+ [M. R. Guthaus, J. E. Stine, S. Ataei, B. Chen, B. Wu, M. Sarwar, "OpenRAM: An Open-Source Memory Compiler," Proceedings of the 35th International Conference on Computer-Aided Design (ICCAD), 2016.](https://escholarship.org/content/qt8x19c778/qt8x19c778_noSplash_b2b3fbbb57f1269f86d0de77865b0691.pdf)
|
||||
+ [S. Ataei, J. Stine, M. Guthaus, “A 64 kb differential single-port 12T SRAM design with a bit-interleaving scheme for low-voltage operation in 32 nm SOI CMOS,” International Conference on Computer Design (ICCD), 2016, pp. 499-506.](https://escholarship.org/uc/item/99f6q9c9)
|
||||
+ [E. Ebrahimi, M. Guthaus, J. Renau, “Timing Speculative SRAM”, IEEE International Symposium on Circuits and Systems (ISCAS), 2017.](https://escholarship.org/content/qt7nn0j5x3/qt7nn0j5x3_noSplash_172457455e1aceba20694c3d7aa489b4.pdf)
|
||||
+ [B. Wu, J.E. Stine, M.R. Guthaus, "Fast and Area-Efficient Word-Line Optimization", IEEE International Symposium on Circuits and Systems (ISCAS), 2019.](https://escholarship.org/content/qt98s4c1hp/qt98s4c1hp_noSplash_753dcc3e218f60aafff98ef77fb56384.pdf)
|
||||
+ [B. Wu, M. Guthaus, "Bottom Up Approach for High Speed SRAM Word-line Buffer Insertion Optimization", IFIP/IEEE International Conference on Very Large Scale Integration (VLSI-SoC), 2019.](https://ieeexplore.ieee.org/document/8920325)
|
||||
+ [H. Nichols, M. Grimes, J. Sowash, J. Cirimelli-Low, M. Guthaus "Automated Synthesis of Multi-Port Memories and Control", IFIP/IEEE International Conference on Very Large Scale Integration (VLSI-SoC), 2019.](https://escholarship.org/content/qt7047n3k0/qt7047n3k0.pdf?t=q4gcij)
|
||||
+ [H. Nichols, "Statistical Modeling of SRAMs", M.S. Thesis, UCSC, 2022.](https://escholarship.org/content/qt7vx9n089/qt7vx9n089_noSplash_cfc4ba479d8eb1b6ec25d7c92357bc18.pdf?t=ra9wzr)
|
||||
+ [M. Guthaus, H. Nichols, J. Cirimelli-Low, J. Kunzler, B. Wu, "Enabling Design Technology Co-Optimization of SRAMs though Open-Source Software", IEEE International Electron Devices Meeting (IEDM), 2020.](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9372047)
|
||||
+ [S. Ataei, J. Stine, M. Guthaus, "A 64 kb differential single-port 12T SRAM design with a bit-interleaving scheme for low-voltage operation in 32 nm SOI CMOS," International Conference on Computer Design (ICCD), 2016, pp. 499-506.](https://escholarship.org/uc/item/99f6q9c9)
|
||||
+ [E. Ebrahimi, M. Guthaus, J. Renau, "Timing Speculative SRAM," IEEE International Symposium on Circuits and Systems (ISCAS), 2017.](https://escholarship.org/content/qt7nn0j5x3/qt7nn0j5x3_noSplash_172457455e1aceba20694c3d7aa489b4.pdf)
|
||||
+ [B. Wu, J.E. Stine, M.R. Guthaus, "Fast and Area-Efficient Word-Line Optimization," IEEE International Symposium on Circuits and Systems (ISCAS), 2019.](https://escholarship.org/content/qt98s4c1hp/qt98s4c1hp_noSplash_753dcc3e218f60aafff98ef77fb56384.pdf)
|
||||
+ [B. Wu, M. Guthaus, "Bottom Up Approach for High Speed SRAM Word-line Buffer Insertion Optimization," IFIP/IEEE International Conference on Very Large Scale Integration (VLSI-SoC), 2019.](https://ieeexplore.ieee.org/document/8920325)
|
||||
+ [H. Nichols, M. Grimes, J. Sowash, J. Cirimelli-Low, M. Guthaus "Automated Synthesis of Multi-Port Memories and Control," IFIP/IEEE International Conference on Very Large Scale Integration (VLSI-SoC), 2019.](https://escholarship.org/content/qt7047n3k0/qt7047n3k0.pdf?t=q4gcij)
|
||||
+ [M. Guthaus, H. Nichols, J. Cirimelli-Low, J. Kunzler, B. Wu, "Enabling Design Technology Co-Optimization of SRAMs though Open-Source Software," IEEE International Electron Devices Meeting (IEDM), 2020.](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9372047)
|
||||
+ [H. Nichols, "Statistical Modeling of SRAMs," M.S. Thesis, UCSC, 2022.](https://escholarship.org/content/qt7vx9n089/qt7vx9n089_noSplash_cfc4ba479d8eb1b6ec25d7c92357bc18.pdf?t=ra9wzr)
|
||||
|
||||
|
||||
|
||||
|
||||
# Contributors & Acknowledgment
|
||||
|
||||
- [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
|
||||
- [James Stine] from [VLSIARCH] co-founded the project.
|
||||
- Many students: Hunter Nichols, Michael Grimes, Jennifer Sowash, Yusu Wang, Joey Kunzler, Jesse Cirimelli-Low, Samira Ataei, Bin Wu, Brian Chen, Jeff Butera
|
||||
- Many students: Hunter Nichols, Michael Grimes, Jennifer Sowash, Yusu Wang, Joey Kunzler, Jesse Cirimelli-Low, Samira Ataei, Bin Wu, Brian Chen, Jeff Butera, Sage Walker
|
||||
|
||||
If I forgot to add you, please let me know!
|
||||
|
||||
|
||||
|
||||
[Matthew Guthaus]: https://users.soe.ucsc.edu/~mrg
|
||||
[Matthew Guthaus]: https://vlsida.github.io
|
||||
[James Stine]: https://ece.okstate.edu/content/stine-james-e-jr-phd
|
||||
[VLSIDA]: https://vlsida.soe.ucsc.edu
|
||||
[VLSIARCH]: https://vlsiarch.ecen.okstate.edu/
|
||||
|
|
@ -88,10 +84,6 @@ If I forgot to add you, please let me know!
|
|||
[Github project]: https://github.com/VLSIDA/OpenRAM
|
||||
|
||||
[documentation]: docs/source/index.md
|
||||
[dev-group]: mailto:openram-dev-group@ucsc.edu
|
||||
[user-group]: mailto:openram-user-group@ucsc.edu
|
||||
[dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu
|
||||
[user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu
|
||||
|
||||
[Klayout]: https://www.klayout.de/
|
||||
[Magic]: http://opencircuitdesign.com/magic/
|
||||
|
|
@ -105,6 +97,5 @@ If I forgot to add you, please let me know!
|
|||
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
|
||||
[Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
|
||||
|
||||
[Slack]: https://join.slack.com/t/openram/shared_invite/zt-onim74ue-zlttW5XI30xvdBlJGJF6JA
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# Attempt to add the source code to the PYTHONPATH here before running globals.init_openram()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .channel_route import *
|
||||
|
|
@ -16,6 +16,7 @@ from .lef import *
|
|||
from .logical_effort import *
|
||||
from .pin_layout import *
|
||||
from .power_data import *
|
||||
from .rom_verilog import *
|
||||
from .route import *
|
||||
from .timing_graph import *
|
||||
from .utils import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -10,7 +10,7 @@ from openram.tech import drc, layer, preferred_directions
|
|||
from openram.tech import layer as tech_layers
|
||||
from .hierarchy_design import hierarchy_design
|
||||
from .vector import vector
|
||||
|
||||
from .utils import ceil
|
||||
|
||||
class contact(hierarchy_design):
|
||||
"""
|
||||
|
|
@ -41,7 +41,7 @@ class contact(hierarchy_design):
|
|||
self.add_comment("implant type: {}\n".format(implant_type))
|
||||
self.add_comment("well_type: {}\n".format(well_type))
|
||||
|
||||
self.is_well_contact = implant_type == well_type
|
||||
self.is_well_contact = (implant_type == well_type) and implant_type is not None
|
||||
|
||||
# If we have a special tap layer, use it
|
||||
self.layer_stack = layer_stack
|
||||
|
|
@ -66,8 +66,6 @@ class contact(hierarchy_design):
|
|||
self.offset = vector(0, 0)
|
||||
self.implant_type = implant_type
|
||||
self.well_type = well_type
|
||||
# Module does not have pins, but has empty pin list.
|
||||
self.pins = []
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
|
|
@ -127,6 +125,8 @@ class contact(hierarchy_design):
|
|||
|
||||
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
|
||||
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
|
||||
self.first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name))
|
||||
|
||||
# If there's a different rule for active
|
||||
# FIXME: Make this more elegant
|
||||
if self.is_well_contact and self.first_layer_name == "active" and "tap_extend_contact" in drc.keys():
|
||||
|
|
@ -137,7 +137,7 @@ class contact(hierarchy_design):
|
|||
self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
|
||||
self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name))
|
||||
self.second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
|
||||
|
||||
self.second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name))
|
||||
# In some technologies, the minimum width may be larger
|
||||
# than the overlap requirement around the via, so
|
||||
# check this for each dimension.
|
||||
|
|
@ -145,7 +145,7 @@ class contact(hierarchy_design):
|
|||
self.first_layer_horizontal_enclosure = max(self.first_layer_enclosure,
|
||||
(self.first_layer_minwidth - self.contact_array_width) / 2)
|
||||
self.first_layer_vertical_enclosure = max(self.first_layer_extend,
|
||||
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
||||
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
||||
elif self.directions[0] == "H":
|
||||
self.first_layer_horizontal_enclosure = max(self.first_layer_extend,
|
||||
(self.first_layer_minwidth - self.contact_array_width) / 2)
|
||||
|
|
@ -168,7 +168,7 @@ class contact(hierarchy_design):
|
|||
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
|
||||
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
||||
else:
|
||||
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
|
||||
debug.error("Invalid second layer direction: ".format(self.directions[1]), -1)
|
||||
|
||||
def create_contact_array(self):
|
||||
""" Create the contact array at the origin"""
|
||||
|
|
@ -223,6 +223,18 @@ class contact(hierarchy_design):
|
|||
first_layer_name = "tap"
|
||||
else:
|
||||
first_layer_name = self.first_layer_name
|
||||
|
||||
area = self.first_layer_width * self.first_layer_height
|
||||
if area < self.first_layer_minarea and self.is_well_contact:
|
||||
if self.directions[0] == "V":
|
||||
area_extend = (self.first_layer_minarea / self.first_layer_width) - self.first_layer_height
|
||||
self.first_layer_height = ceil(self.first_layer_height + area_extend)
|
||||
self.first_layer_position = self.first_layer_position - vector(0, area_extend / 2)
|
||||
elif self.directions[0] == "H":
|
||||
area_extend = (self.first_layer_minarea / self.first_layer_height) - self.first_layer_width
|
||||
self.first_layer_width = ceil(self.first_layer_height + area_extend)
|
||||
self.first_layer_position = self.first_layer_position - vector(area_extend / 2, 0)
|
||||
|
||||
self.add_rect(layer=first_layer_name,
|
||||
offset=self.first_layer_position,
|
||||
width=self.first_layer_width,
|
||||
|
|
@ -238,6 +250,7 @@ class contact(hierarchy_design):
|
|||
self.second_layer_minwidth)
|
||||
self.second_layer_height = max(self.contact_array_height + 2 * self.second_layer_vertical_enclosure,
|
||||
self.second_layer_minwidth)
|
||||
|
||||
self.add_rect(layer=self.second_layer_name,
|
||||
offset=self.second_layer_position,
|
||||
width=self.second_layer_width,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -41,17 +41,17 @@ class design(hierarchy_design):
|
|||
if prop and prop.hard_cell:
|
||||
# The pins get added from the spice file, so just check
|
||||
# that they matched here
|
||||
debug.check(prop.port_names == self.pins,
|
||||
"Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins))
|
||||
debug.check(prop.port_names == list(self.pins),
|
||||
"Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, list(self.pins)))
|
||||
self.add_pin_indices(prop.port_indices)
|
||||
self.add_pin_names(prop.port_map)
|
||||
self.add_pin_types(prop.port_types)
|
||||
self.update_pin_types(prop.port_types)
|
||||
|
||||
|
||||
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||
GDS["unit"],
|
||||
layer[prop.boundary_layer])
|
||||
self.pin_map = utils.get_libcell_pins(self.pins,
|
||||
self.pin_map = utils.get_libcell_pins(list(self.pins),
|
||||
self.cell_name,
|
||||
GDS["unit"])
|
||||
|
||||
|
|
@ -126,5 +126,3 @@ class design(hierarchy_design):
|
|||
for inst in self.insts:
|
||||
total_module_power += inst.mod.analytical_power(corner, load)
|
||||
return total_module_power
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -161,8 +161,8 @@ class geometry:
|
|||
|
||||
class instance(geometry):
|
||||
"""
|
||||
An instance of an instance/module with a specified location and
|
||||
rotation
|
||||
An instance of a module with a specified location, rotation,
|
||||
spice pins, and spice nets
|
||||
"""
|
||||
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
||||
"""Initializes an instance to represent a module"""
|
||||
|
|
@ -176,6 +176,18 @@ class instance(geometry):
|
|||
self.rotate = rotate
|
||||
self.offset = vector(offset).snap_to_grid()
|
||||
self.mirror = mirror
|
||||
# track if the instance's spice pin connections have been made
|
||||
self.connected = False
|
||||
|
||||
# deepcopy because this instance needs to
|
||||
# change attributes in these spice objects
|
||||
self.spice_pins = copy.deepcopy(self.mod.pins)
|
||||
self.spice_nets = copy.deepcopy(self.mod.nets)
|
||||
for pin in self.spice_pins.values():
|
||||
pin.set_inst(self)
|
||||
for net in self.spice_nets.values():
|
||||
net.set_inst(self)
|
||||
|
||||
if OPTS.netlist_only:
|
||||
self.width = 0
|
||||
self.height = 0
|
||||
|
|
@ -274,6 +286,34 @@ class instance(geometry):
|
|||
new_pins.append(p)
|
||||
return new_pins
|
||||
|
||||
def connect_spice_pins(self, nets_list):
|
||||
"""
|
||||
add the connection between instance pins and module nets
|
||||
to both of their respective objects
|
||||
nets_list must be the same length as self.spice_pins
|
||||
"""
|
||||
if len(nets_list) == 0 and len(self.spice_pins) == 0:
|
||||
# this is the only valid case to skip the following debug check
|
||||
# because this with no pins are often connected arbitrarily
|
||||
self.connected = True
|
||||
return
|
||||
debug.check(not self.connected,
|
||||
"instance {} has already been connected".format(self.name))
|
||||
debug.check(len(self.spice_pins) == len(nets_list),
|
||||
"must provide list of nets the same length as pin list\
|
||||
when connecting an instance")
|
||||
for pin in self.spice_pins.values():
|
||||
net = nets_list.pop(0)
|
||||
pin.set_inst_net(net)
|
||||
net.connect_pin(pin)
|
||||
self.connected = True
|
||||
|
||||
def get_connections(self):
|
||||
conns = []
|
||||
for pin in self.spice_pins.values():
|
||||
conns.append(pin.inst_net.name)
|
||||
return conns
|
||||
|
||||
def calculate_transform(self, node):
|
||||
#set up the rotation matrix
|
||||
angle = math.radians(float(node.rotate))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -135,11 +135,11 @@ class hierarchy_design(spice, layout):
|
|||
# Translate port names to external nets
|
||||
if len(port_nets) != len(self.pins):
|
||||
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
|
||||
self.pins),
|
||||
list(self.pins)),
|
||||
1)
|
||||
port_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||
port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
|
||||
debug.info(3, "Instance name={}".format(inst_name))
|
||||
for subinst, conns in zip(self.insts, self.conns):
|
||||
for subinst, conns in zip(self.insts, self.get_instance_connections()):
|
||||
if subinst in self.graph_inst_exclude:
|
||||
continue
|
||||
subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name
|
||||
|
|
@ -153,11 +153,11 @@ class hierarchy_design(spice, layout):
|
|||
# Translate port names to external nets
|
||||
if len(port_nets) != len(self.pins):
|
||||
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
|
||||
self.pins),
|
||||
list(self.pins)),
|
||||
1)
|
||||
port_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||
port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
|
||||
debug.info(3, "Instance name={}".format(inst_name))
|
||||
for subinst, conns in zip(self.insts, self.conns):
|
||||
for subinst, conns in zip(self.insts, self.get_instance_connections()):
|
||||
subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name
|
||||
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
|
||||
for si_port, conn in zip(subinst_ports, conns):
|
||||
|
|
@ -186,7 +186,7 @@ class hierarchy_design(spice, layout):
|
|||
"""
|
||||
# The final pin names will depend on the spice hierarchy, so
|
||||
# they are passed as an input.
|
||||
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||
pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
|
||||
input_pins = self.get_inputs()
|
||||
output_pins = self.get_outputs()
|
||||
inout_pins = self.get_inouts()
|
||||
|
|
@ -197,7 +197,7 @@ class hierarchy_design(spice, layout):
|
|||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
pins = ",".join(self.pins)
|
||||
pins = ",".join(list(self.pins))
|
||||
insts = [" {}".format(x) for x in self.insts]
|
||||
objs = [" {}".format(x) for x in self.objs]
|
||||
s = "********** design {0} **********".format(self.cell_name)
|
||||
|
|
@ -208,7 +208,7 @@ class hierarchy_design(spice, layout):
|
|||
|
||||
def __repr__(self):
|
||||
""" override print function output """
|
||||
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n"
|
||||
text="( design: " + self.name + " pins=" + str(list(self.pins)) + " " + str(self.width) + "x" + str(self.height) + " )\n"
|
||||
for i in self.objs:
|
||||
text+=str(i) + ",\n"
|
||||
for i in self.insts:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -22,7 +22,7 @@ from openram.sram_factory import factory
|
|||
from openram import OPTS
|
||||
from .vector import vector
|
||||
from .pin_layout import pin_layout
|
||||
from .utils import round_to_grid
|
||||
from .utils import round_to_grid, ceil
|
||||
from . import geometry
|
||||
|
||||
try:
|
||||
|
|
@ -187,7 +187,7 @@ class layout():
|
|||
pass
|
||||
|
||||
# Skip computing the pitch for non-routing layers
|
||||
if layer_id in ["active", "nwell"]:
|
||||
if layer_id in ["active", "nwell", "pwell"]:
|
||||
continue
|
||||
|
||||
# Add the pitch
|
||||
|
|
@ -259,8 +259,7 @@ class layout():
|
|||
contact_width = contact1.first_layer_width
|
||||
layer_space = getattr(layout, layer1 + "_space")
|
||||
|
||||
#print(layer_stack)
|
||||
#print(contact1)
|
||||
|
||||
pitch = contact_width + layer_space
|
||||
|
||||
return round_to_grid(pitch)
|
||||
|
|
@ -629,7 +628,7 @@ class layout():
|
|||
"""
|
||||
Return a pin list of all pins
|
||||
"""
|
||||
return self.pins
|
||||
return list(self.pins)
|
||||
|
||||
def copy_layout_pin(self, instance, pin_name, new_name="", relative_offset=vector(0, 0)):
|
||||
"""
|
||||
|
|
@ -838,8 +837,7 @@ class layout():
|
|||
|
||||
from_id = tech_layer_indices[from_layer]
|
||||
to_id = tech_layer_indices[to_layer]
|
||||
|
||||
layer_list = [x for x in tech_layer_indices.keys() if tech_layer_indices[x] >= from_id and tech_layer_indices[x] < to_id]
|
||||
layer_list = [x for x in tech_layer_indices.keys() if tech_layer_indices[x] > from_id and tech_layer_indices[x] < to_id]
|
||||
|
||||
return layer_list
|
||||
|
||||
|
|
@ -944,14 +942,19 @@ class layout():
|
|||
|
||||
return (bot_rect, top_rect)
|
||||
|
||||
def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True):
|
||||
def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True, new_name=None):
|
||||
"""
|
||||
Route together all of the pins of a given name that horizontally align.
|
||||
Uses local_insts if insts not specified.
|
||||
Uses center of pin by default, or top or botom if specified.
|
||||
New top level pin can be renamed with new_name, otherwise the new pin will keep the same name as old pins
|
||||
TODO: Add equally spaced option for IR drop min, right now just 2
|
||||
"""
|
||||
|
||||
if new_name is not None:
|
||||
pin_name = new_name
|
||||
else:
|
||||
pin_name = name
|
||||
|
||||
bins = {}
|
||||
if not insts:
|
||||
|
|
@ -1011,16 +1014,17 @@ class layout():
|
|||
left_pos = vector(left_x + 0.5 * via_width, y)
|
||||
right_pos = vector(right_x + 0.5 * via_width, y)
|
||||
|
||||
# self.add_layout_pin_rect_ends(name=name,
|
||||
# layer=pin_layer,
|
||||
# start=left_pos,
|
||||
# end=right_pos,
|
||||
# width=via_height)
|
||||
self.add_layout_pin_segment_center(text=name,
|
||||
layer=pin_layer,
|
||||
start=left_pos,
|
||||
end=right_pos,
|
||||
width=via_height)
|
||||
# self.add_layout_pin_rect_ends(name=name,
|
||||
# layer=pin_layer,
|
||||
# start=left_pos,
|
||||
# end=right_pos,
|
||||
# width=via_height)
|
||||
|
||||
self.add_layout_pin_segment_center(text=pin_name,
|
||||
layer=pin_layer,
|
||||
start=left_pos,
|
||||
end=right_pos,
|
||||
width=via_height)
|
||||
|
||||
def add_layout_end_pin_segment_center(self, text, layer, start, end):
|
||||
"""
|
||||
|
|
@ -1368,12 +1372,11 @@ class layout():
|
|||
min_width = drc("minwidth_{}".format(layer))
|
||||
|
||||
if preferred_directions[layer] == "V":
|
||||
new_height = max(min_area / width, min_width)
|
||||
new_height = ceil(max(min_area / width, min_width))
|
||||
new_width = width
|
||||
else:
|
||||
new_width = max(min_area / height, min_width)
|
||||
new_width = ceil(max(min_area / height, min_width))
|
||||
new_height = height
|
||||
|
||||
debug.check(min_area <= round_to_grid(new_height*new_width), "Min area violated.")
|
||||
|
||||
self.add_rect_center(layer=layer,
|
||||
|
|
@ -1523,6 +1526,7 @@ class layout():
|
|||
""" Return the pin shapes as blockages for non-top-level blocks. """
|
||||
# FIXME: We don't have a body contact in ptx, so just ignore it for now
|
||||
import copy
|
||||
# FIXME: this may not work now that self.pins is a dict as defined in hierarchy_spice
|
||||
pin_names = copy.deepcopy(self.pins)
|
||||
if self.name.startswith("pmos") or self.name.startswith("nmos"):
|
||||
pin_names.remove("B")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -13,6 +13,7 @@ from pprint import pformat
|
|||
from openram import debug
|
||||
from openram import tech
|
||||
from openram import OPTS
|
||||
from collections import OrderedDict
|
||||
from .delay_data import delay_data
|
||||
from .wire_spice_model import wire_spice_model
|
||||
from .power_data import power_data
|
||||
|
|
@ -49,26 +50,24 @@ class spice():
|
|||
if not os.path.exists(self.lvs_file):
|
||||
self.lvs_file = self.sp_file
|
||||
|
||||
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
|
||||
# Holds subckts/mods for this module
|
||||
self.mods = set()
|
||||
# Holds the pins for this module (in order)
|
||||
self.pins = []
|
||||
# The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND
|
||||
# for each instance, this is the set of nets/nodes that map to the pins for this instance
|
||||
self.pin_type = {}
|
||||
# on Python3.7+ regular dictionaries guarantee order too, but we allow use of v3.5+
|
||||
self.pins = OrderedDict()
|
||||
# An (optional) list of indices to reorder the pins to match the spice.
|
||||
self.pin_indices = []
|
||||
# THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
|
||||
# Spice format)
|
||||
self.conns = []
|
||||
# If this is set, it will out output subckt or isntances of this (for row/col caps etc.)
|
||||
# internal nets, which may or may not be connected to pins of the same name
|
||||
self.nets = {}
|
||||
# If this is set, it will not output subckt or instances of this (for row/col caps etc.)
|
||||
self.no_instances = False
|
||||
# If we are doing a trimmed netlist, these are the instance that will be filtered
|
||||
self.trim_insts = set()
|
||||
# Keep track of any comments to add the the spice
|
||||
try:
|
||||
self.commments
|
||||
self.comments
|
||||
except AttributeError:
|
||||
self.comments = []
|
||||
|
||||
|
|
@ -82,7 +81,7 @@ class spice():
|
|||
""" Add a comment to the spice file """
|
||||
|
||||
try:
|
||||
self.commments
|
||||
self.comments
|
||||
except AttributeError:
|
||||
self.comments = []
|
||||
|
||||
|
|
@ -90,128 +89,114 @@ class spice():
|
|||
|
||||
def add_pin(self, name, pin_type="INOUT"):
|
||||
""" Adds a pin to the pins list. Default type is INOUT signal. """
|
||||
self.pins.append(name)
|
||||
self.pin_type[name]=pin_type
|
||||
debug.check(pin_type in self.valid_signal_types,
|
||||
"Invalid signaltype for {0}: {1}".format(name,
|
||||
pin_type))
|
||||
debug.check(name not in self.pins, "cannot add duplicate spice pin {}".format(name))
|
||||
self.pins[name] = pin_spice(name, pin_type, self)
|
||||
|
||||
def add_pin_list(self, pin_list, pin_type="INOUT"):
|
||||
""" Adds a pin_list to the pins list """
|
||||
# The type list can be a single type for all pins
|
||||
# The pin type list can be a single type for all pins
|
||||
# or a list that is the same length as the pin list.
|
||||
if type(pin_type)==str:
|
||||
if isinstance(pin_type, str):
|
||||
for pin in pin_list:
|
||||
debug.check(pin_type in self.valid_signal_types,
|
||||
"Invalid signaltype for {0}: {1}".format(pin,
|
||||
pin_type))
|
||||
self.add_pin(pin, pin_type)
|
||||
|
||||
elif len(pin_type)==len(pin_list):
|
||||
for (pin, ptype) in zip(pin_list, pin_type):
|
||||
debug.check(ptype in self.valid_signal_types,
|
||||
"Invalid signaltype for {0}: {1}".format(pin,
|
||||
ptype))
|
||||
self.add_pin(pin, ptype)
|
||||
for (pin, type) in zip(pin_list, pin_type):
|
||||
self.add_pin(pin, type)
|
||||
else:
|
||||
debug.error("Mismatch in type and pin list lengths.", -1)
|
||||
debug.error("Pin type must be a string or list of strings the same length as pin_list", -1)
|
||||
|
||||
def add_pin_indices(self, index_list):
|
||||
"""
|
||||
Add pin indices for all the cell's pins.
|
||||
"""
|
||||
""" Add pin indices for all the cell's pins. """
|
||||
self.pin_indices = index_list
|
||||
|
||||
def get_ordered_inputs(self, input_list):
|
||||
"""
|
||||
Return the inputs reordered to match the pins.
|
||||
"""
|
||||
""" Return the inputs reordered to match the pins. """
|
||||
if not self.pin_indices:
|
||||
return input_list
|
||||
|
||||
new_list = [input_list[x] for x in self.pin_indices]
|
||||
return new_list
|
||||
|
||||
def add_pin_types(self, type_list):
|
||||
"""
|
||||
Add pin types for all the cell's pins.
|
||||
"""
|
||||
# This only works if self.pins == bitcell.pin_names
|
||||
if len(type_list) != len(self.pins):
|
||||
debug.error("{} spice subcircuit number of port types does not match number of pins\
|
||||
\n SPICE names={}\
|
||||
\n Module names={}\
|
||||
".format(self.name, self.pins, type_list), 1)
|
||||
self.pin_type = {pin: type for pin, type in zip(self.pins, type_list)}
|
||||
def update_pin_types(self, type_list):
|
||||
""" Change pin types for all the cell's pins. """
|
||||
debug.check(len(type_list) == len(self.pins),
|
||||
"{} spice subcircuit number of port types does not match number of pins\
|
||||
\n pin names={}\n port types={}".format(self.name, list(self.pins), type_list))
|
||||
for pin, type in zip(self.pins.values(), type_list):
|
||||
pin.set_type(type)
|
||||
|
||||
def get_pin_type(self, name):
|
||||
""" Returns the type of the signal pin. """
|
||||
pin_type = self.pin_type[name]
|
||||
debug.check(pin_type in self.valid_signal_types,
|
||||
"Invalid signaltype for {0}: {1}".format(name, pin_type))
|
||||
return pin_type
|
||||
pin = self.pins.get(name)
|
||||
debug.check(pin is not None, "Spice pin {} not found".format(name))
|
||||
return pin.type
|
||||
|
||||
def get_pin_dir(self, name):
|
||||
""" Returns the direction of the pin. (Supply/ground are INOUT). """
|
||||
if self.pin_type[name] in ["POWER", "GROUND"]:
|
||||
pin_type = self.get_pin_type(name)
|
||||
if pin_type in ["POWER", "GROUND"]:
|
||||
return "INOUT"
|
||||
else:
|
||||
return self.pin_type[name]
|
||||
return pin_type
|
||||
|
||||
def get_inputs(self):
|
||||
""" These use pin types to determine pin lists. These
|
||||
may be over-ridden by submodules that didn't use pin directions yet."""
|
||||
"""
|
||||
These use pin types to determine pin lists.
|
||||
Returns names only, to maintain historical interface.
|
||||
"""
|
||||
input_list = []
|
||||
for pin in self.pins:
|
||||
if self.pin_type[pin]=="INPUT":
|
||||
input_list.append(pin)
|
||||
for pin in self.pins.values():
|
||||
if pin.type == "INPUT":
|
||||
input_list.append(pin.name)
|
||||
return input_list
|
||||
|
||||
def get_outputs(self):
|
||||
""" These use pin types to determine pin lists. These
|
||||
may be over-ridden by submodules that didn't use pin directions yet."""
|
||||
"""
|
||||
These use pin types to determine pin lists.
|
||||
Returns names only, to maintain historical interface.
|
||||
"""
|
||||
output_list = []
|
||||
for pin in self.pins:
|
||||
if self.pin_type[pin]=="OUTPUT":
|
||||
output_list.append(pin)
|
||||
for pin in self.pins.values():
|
||||
if pin.type == "OUTPUT":
|
||||
output_list.append(pin.name)
|
||||
return output_list
|
||||
|
||||
def get_inouts(self):
|
||||
"""
|
||||
These use pin types to determine pin lists.
|
||||
Returns names only, to maintain historical interface.
|
||||
"""
|
||||
inout_list = []
|
||||
for pin in self.pins.values():
|
||||
if pin.type == "INOUT":
|
||||
inout_list.append(pin.name)
|
||||
return inout_list
|
||||
|
||||
def copy_pins(self, other_module, suffix=""):
|
||||
""" This will copy all of the pins from the other module and add an optional suffix."""
|
||||
for pin in other_module.pins:
|
||||
self.add_pin(pin + suffix, other_module.get_pin_type(pin))
|
||||
for pin in other_module.pins.values():
|
||||
self.add_pin(pin.name + suffix, pin.type)
|
||||
|
||||
def get_inouts(self):
|
||||
""" These use pin types to determine pin lists. These
|
||||
may be over-ridden by submodules that didn't use pin directions yet."""
|
||||
inout_list = []
|
||||
for pin in self.pins:
|
||||
if self.pin_type[pin]=="INOUT":
|
||||
inout_list.append(pin)
|
||||
return inout_list
|
||||
|
||||
def connect_inst(self, args, check=True):
|
||||
def connect_inst(self, args):
|
||||
"""
|
||||
Connects the pins of the last instance added
|
||||
It is preferred to use the function with the check to find if
|
||||
there is a problem. The check option can be set to false
|
||||
where we dynamically generate groups of connections after a
|
||||
group of modules are generated.
|
||||
"""
|
||||
|
||||
num_pins = len(self.insts[-1].mod.pins)
|
||||
spice_pins = list(self.insts[-1].spice_pins)
|
||||
num_pins = len(spice_pins)
|
||||
num_args = len(args)
|
||||
|
||||
# Order the arguments if the hard cell has a custom port order
|
||||
ordered_args = self.get_ordered_inputs(args)
|
||||
|
||||
if (check and num_pins != num_args):
|
||||
if (num_pins != num_args):
|
||||
if num_pins < num_args:
|
||||
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
|
||||
mod_pins = spice_pins + [""] * (num_args - num_pins)
|
||||
arg_pins = ordered_args
|
||||
else:
|
||||
arg_pins = ordered_args + [""] * (num_pins - num_args)
|
||||
mod_pins = self.insts[-1].mod.pins
|
||||
mod_pins = spice_pins
|
||||
|
||||
modpins_string = "\n".join(["{0} -> {1}".format(arg, mod) for (arg, mod) in zip(arg_pins, mod_pins)])
|
||||
debug.error("Connection mismatch:\nInst ({0}) -> Mod ({1})\n{2}".format(num_args,
|
||||
|
|
@ -219,27 +204,17 @@ class spice():
|
|||
modpins_string),
|
||||
1)
|
||||
|
||||
self.conns.append(ordered_args)
|
||||
ordered_nets = self.create_nets(ordered_args)
|
||||
self.insts[-1].connect_spice_pins(ordered_nets)
|
||||
|
||||
# This checks if we don't have enough instance port connections for the number of insts
|
||||
if check and (len(self.insts)!=len(self.conns)):
|
||||
insts_string=pformat(self.insts)
|
||||
conns_string=pformat(self.conns)
|
||||
|
||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
|
||||
len(self.insts),
|
||||
len(self.conns)))
|
||||
debug.error("Instances: \n" + str(insts_string))
|
||||
debug.error("-----")
|
||||
debug.error("Connections: \n" + str(conns_string), 1)
|
||||
|
||||
def get_conns(self, inst):
|
||||
"""Returns the connections of a given instance."""
|
||||
for i in range(len(self.insts)):
|
||||
if inst is self.insts[i]:
|
||||
return self.conns[i]
|
||||
# If not found, returns None
|
||||
return None
|
||||
def create_nets(self, names_list):
|
||||
nets = []
|
||||
for name in names_list:
|
||||
# setdefault adds to the dict if it doesn't find the net in it already
|
||||
# then it returns the net it found or created, a net_spice object
|
||||
net = self.nets.setdefault(name, net_spice(name, self))
|
||||
nets.append(net)
|
||||
return nets
|
||||
|
||||
def sp_read(self):
|
||||
"""
|
||||
|
|
@ -258,7 +233,7 @@ class spice():
|
|||
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
|
||||
subckt_line = list(filter(subckt.search, self.spice))[0]
|
||||
# parses line into ports and remove subckt
|
||||
self.pins = subckt_line.split(" ")[2:]
|
||||
self.add_pin_list(subckt_line.split(" ")[2:])
|
||||
else:
|
||||
debug.info(4, "no spfile {0}".format(self.sp_file))
|
||||
self.spice = []
|
||||
|
|
@ -279,10 +254,10 @@ class spice():
|
|||
subckt_line = list(filter(subckt.search, self.lvs))[0]
|
||||
# parses line into ports and remove subckt
|
||||
lvs_pins = subckt_line.split(" ")[2:]
|
||||
debug.check(lvs_pins == self.pins,
|
||||
debug.check(lvs_pins == list(self.pins),
|
||||
"Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins,
|
||||
self.lvs_file,
|
||||
self.pins,
|
||||
list(self.pins),
|
||||
self.sp_file))
|
||||
|
||||
def check_net_in_spice(self, net_name):
|
||||
|
|
@ -327,78 +302,72 @@ class spice():
|
|||
# If spice isn't defined, we dynamically generate one.
|
||||
|
||||
# recursively write the modules
|
||||
for i in self.mods:
|
||||
if self.contains(i, usedMODS):
|
||||
for mod in self.mods:
|
||||
if self.contains(mod, usedMODS):
|
||||
continue
|
||||
usedMODS.append(i)
|
||||
i.sp_write_file(sp, usedMODS, lvs, trim)
|
||||
usedMODS.append(mod)
|
||||
mod.sp_write_file(sp, usedMODS, lvs, trim)
|
||||
|
||||
if len(self.insts) == 0:
|
||||
return
|
||||
if self.pins == []:
|
||||
if len(self.pins) == 0:
|
||||
return
|
||||
|
||||
# write out the first spice line (the subcircuit)
|
||||
wrapped_pins = "\n+ ".join(tr.wrap(" ".join(self.pins)))
|
||||
wrapped_pins = "\n+ ".join(tr.wrap(" ".join(list(self.pins))))
|
||||
sp.write("\n.SUBCKT {0}\n+ {1}\n".format(self.cell_name,
|
||||
wrapped_pins))
|
||||
|
||||
# write a PININFO line
|
||||
if False:
|
||||
pin_info = "*.PININFO"
|
||||
for pin in self.pins:
|
||||
if self.pin_type[pin] == "INPUT":
|
||||
pin_info += " {0}:I".format(pin)
|
||||
elif self.pin_type[pin] == "OUTPUT":
|
||||
pin_info += " {0}:O".format(pin)
|
||||
else:
|
||||
pin_info += " {0}:B".format(pin)
|
||||
sp.write(pin_info + "\n")
|
||||
|
||||
# Also write pins as comments
|
||||
for pin in self.pins:
|
||||
sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin]))
|
||||
for pin in self.pins.values():
|
||||
sp.write("* {1:6}: {0} \n".format(pin.name, pin.type))
|
||||
|
||||
for line in self.comments:
|
||||
sp.write("* {}\n".format(line))
|
||||
|
||||
# every instance must have a set of connections, even if it is empty.
|
||||
if len(self.insts) != len(self.conns):
|
||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name,
|
||||
len(self.insts),
|
||||
len(self.conns)))
|
||||
debug.error("Instances: \n" + str(self.insts))
|
||||
debug.error("-----")
|
||||
debug.error("Connections: \n" + str(self.conns), 1)
|
||||
# every instance must be connected with the connect_inst function
|
||||
# TODO: may run into empty pin lists edge case, not sure yet
|
||||
connected = True
|
||||
for inst in self.insts:
|
||||
if inst.connected:
|
||||
continue
|
||||
debug.error("Instance {} spice pins not connected".format(str(inst)))
|
||||
connected = False
|
||||
debug.check(connected, "{0} : Not all instance spice pins are connected.".format(self.cell_name))
|
||||
|
||||
for i in range(len(self.insts)):
|
||||
for inst in self.insts:
|
||||
# we don't need to output connections of empty instances.
|
||||
# these are wires and paths
|
||||
if self.conns[i] == []:
|
||||
if len(inst.spice_pins) == 0:
|
||||
continue
|
||||
|
||||
# Instance with no devices in it needs no subckt/instance
|
||||
if self.insts[i].mod.no_instances:
|
||||
if inst.mod.no_instances:
|
||||
continue
|
||||
|
||||
# If this is a trimmed netlist, skip it by adding comment char
|
||||
if trim and self.insts[i].name in self.trim_insts:
|
||||
if trim and inst.name in self.trim_insts:
|
||||
sp.write("* ")
|
||||
|
||||
if lvs and hasattr(self.insts[i].mod, "lvs_device"):
|
||||
sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
if lvs and hasattr(inst.mod, "lvs_device"):
|
||||
sp.write(inst.mod.lvs_device.format(inst.name,
|
||||
" ".join(inst.get_connections())))
|
||||
sp.write("\n")
|
||||
elif hasattr(self.insts[i].mod, "spice_device"):
|
||||
sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
elif hasattr(inst.mod, "spice_device"):
|
||||
sp.write(inst.mod.spice_device.format(inst.name,
|
||||
" ".join(inst.get_connections())))
|
||||
sp.write("\n")
|
||||
else:
|
||||
wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i])))
|
||||
|
||||
sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name,
|
||||
wrapped_connections,
|
||||
self.insts[i].mod.cell_name))
|
||||
if trim and inst.name in self.trim_insts:
|
||||
wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(inst.get_connections())))
|
||||
sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(inst.name,
|
||||
wrapped_connections,
|
||||
inst.mod.cell_name))
|
||||
else:
|
||||
wrapped_connections = "\n+ ".join(tr.wrap(" ".join(inst.get_connections())))
|
||||
sp.write("X{0}\n+ {1}\n+ {2}\n".format(inst.name,
|
||||
wrapped_connections,
|
||||
inst.mod.cell_name))
|
||||
|
||||
sp.write(".ENDS {0}\n".format(self.cell_name))
|
||||
|
||||
|
|
@ -727,6 +696,12 @@ class spice():
|
|||
aliases.append(net)
|
||||
return aliases
|
||||
|
||||
def get_instance_connections(self):
|
||||
conns = []
|
||||
for inst in self.insts:
|
||||
conns.append(inst.get_connections())
|
||||
return conns
|
||||
|
||||
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
||||
"""
|
||||
Checks if the alias_net in input mod is the same as the input net for this mod (self).
|
||||
|
|
@ -739,7 +714,7 @@ class spice():
|
|||
return True
|
||||
# Check connections of all other subinsts
|
||||
mod_set = set()
|
||||
for subinst, inst_conns in zip(self.insts, self.conns):
|
||||
for subinst, inst_conns in zip(self.insts, self.get_instance_connections()):
|
||||
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins):
|
||||
if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
|
||||
return True
|
||||
|
|
@ -756,3 +731,149 @@ class spice():
|
|||
return self == mod and \
|
||||
child_net.lower() == alias_net.lower() and \
|
||||
parent_net.lower() == alias_net.lower()
|
||||
|
||||
|
||||
class pin_spice():
|
||||
"""
|
||||
A class to represent a spice netlist pin.
|
||||
mod is the parent module that created this pin.
|
||||
mod_net is the net object of this pin's parent module. It must have the same name as the pin.
|
||||
inst is the instance this pin is a part of, if any.
|
||||
inst_net is the net object from mod's nets which connects to this pin.
|
||||
"""
|
||||
|
||||
valid_pin_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND", "BIAS"]
|
||||
|
||||
def __init__(self, name, type, mod):
|
||||
self.name = name
|
||||
self.set_type(type)
|
||||
self.mod = mod
|
||||
self.mod_net = None
|
||||
self.inst = None
|
||||
self.inst_net = None
|
||||
|
||||
# TODO: evaluate if this makes sense... and works
|
||||
self._hash = hash(self.name)
|
||||
|
||||
def set_type(self, type):
|
||||
debug.check(type in pin_spice.valid_pin_types,
|
||||
"Invalid pin type for {0}: {1}".format(self.name, type))
|
||||
self.type = type
|
||||
|
||||
def set_mod_net(self, net):
|
||||
debug.check(isinstance(net, net_spice), "net must be a net_spice object")
|
||||
debug.check(net.name == self.name, "module spice net must have same name as spice pin")
|
||||
self.mod_net = net
|
||||
|
||||
def set_inst(self, inst):
|
||||
self.inst = inst
|
||||
|
||||
def set_inst_net(self, net):
|
||||
if self.inst_net is not None:
|
||||
debug.error("pin {} is already connected to net {}\
|
||||
so it cannot also be connected to net {}\
|
||||
".format(self.name, self.inst_net.name, net.name), 1)
|
||||
debug.check(isinstance(net, net_spice), "net must be a net_spice object")
|
||||
self.inst_net = net
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "(pin_name={} type={})".format(self.name, self.type)
|
||||
|
||||
def __repr__(self):
|
||||
""" override repr function output """
|
||||
return self.name
|
||||
|
||||
def __eq__(self, name):
|
||||
return (name == self.name) if isinstance(name, str) else super().__eq__(name)
|
||||
|
||||
def __hash__(self):
|
||||
"""
|
||||
Implement the hash function for sets etc.
|
||||
Only hash name since spice does not allow two pins to share a name.
|
||||
Provides a speedup if pin_spice is used as a key for dicts.
|
||||
"""
|
||||
return self._hash
|
||||
|
||||
def __deepcopy__(original, memo):
|
||||
"""
|
||||
This function is defined so that instances of modules can make deep
|
||||
copies of their parent module's pins dictionary. It is only expected
|
||||
to be called by the instance class __init__ function. Mod and mod_net
|
||||
should not be deep copies but references to the existing mod and net
|
||||
objects they refer to in the original. If inst is already defined this
|
||||
function will throw an error because that means it was called on a pin
|
||||
from an instance, which is not defined behavior.
|
||||
"""
|
||||
debug.check(original.inst is None,
|
||||
"cannot make a deepcopy of a spice pin from an inst")
|
||||
pin = pin_spice(original.name, original.type, original.mod)
|
||||
if original.mod_net is not None:
|
||||
pin.set_mod_net(original.mod_net)
|
||||
return pin
|
||||
|
||||
|
||||
class net_spice():
|
||||
"""
|
||||
A class to represent a spice net.
|
||||
mod is the parent module that created this net.
|
||||
pins are all the pins connected to this net.
|
||||
inst is the instance this net is a part of, if any.
|
||||
"""
|
||||
|
||||
def __init__(self, name, mod):
|
||||
self.name = name
|
||||
self.pins = []
|
||||
self.mod = mod
|
||||
self.inst = None
|
||||
|
||||
# TODO: evaluate if this makes sense... and works
|
||||
self._hash = hash(self.name)
|
||||
|
||||
def connect_pin(self, pin):
|
||||
debug.check(isinstance(pin, pin_spice), "pin must be a pin_spice object")
|
||||
if pin in self.pins:
|
||||
debug.warning("pin {} was already connected to net {} ... why was it connected again?".format(pin.name, self.name))
|
||||
else:
|
||||
self.pins.append(pin)
|
||||
|
||||
def set_inst(self, inst):
|
||||
self.inst = inst
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "(net_name={} type={})".format(self.name, self.type)
|
||||
|
||||
def __repr__(self):
|
||||
""" override repr function output """
|
||||
return self.name
|
||||
|
||||
def __eq__(self, name):
|
||||
return (name == self.name) if isinstance(name, str) else super().__eq__(name)
|
||||
|
||||
def __hash__(self):
|
||||
"""
|
||||
Implement the hash function for sets etc.
|
||||
Only hash name since spice does not allow two nets to share a name
|
||||
(on the same level of hierarchy, or rather they will be the same net).
|
||||
Provides a speedup if net_spice is used as a key for dicts.
|
||||
"""
|
||||
return self._hash
|
||||
|
||||
def __deepcopy__(original, memo):
|
||||
"""
|
||||
This function is defined so that instances of modules can make deep
|
||||
copies of their parent module's nets dictionary. It is only expected
|
||||
to be called by the instance class __init__ function. Mod
|
||||
should not be a deep copy but a reference to the existing mod
|
||||
object it refers to in the original. If inst is already defined this
|
||||
function will throw an error because that means it was called on a net
|
||||
from an instance, which is not defined behavior.
|
||||
"""
|
||||
debug.check(original.inst is None,
|
||||
"cannot make a deepcopy of a spice net from an inst")
|
||||
net = net_spice(original.name, original.mod)
|
||||
if original.pins != []:
|
||||
# TODO: honestly I'm not sure if this is right but we'll see...
|
||||
net.pins = original.pins
|
||||
return net
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -18,7 +18,7 @@ class lef:
|
|||
"""
|
||||
SRAM LEF Class open GDS file, read pins information, obstruction
|
||||
and write them to LEF file.
|
||||
This is inherited by the sram_base class.
|
||||
This is inherited by the sram_1bank class.
|
||||
"""
|
||||
def __init__(self, layers):
|
||||
# LEF db units per micron
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import math
|
||||
from openram.tech import spice
|
||||
|
||||
|
||||
class rom_verilog:
|
||||
"""
|
||||
Create a behavioral Verilog file for simulation.
|
||||
This is inherited by the rom_base class.
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def verilog_write(self, verilog_name):
|
||||
""" Write a behavioral Verilog model. """
|
||||
self.vf = open(verilog_name, "w")
|
||||
|
||||
self.vf.write("// OpenROM ROM model\n")
|
||||
|
||||
#basic info
|
||||
self.vf.write("// Words: {0}\n".format(self.num_words))
|
||||
self.vf.write("// Word size: {0}\n".format(self.word_size))
|
||||
self.vf.write("// Word per Row: {0}\n".format(self.words_per_row))
|
||||
self.vf.write("// Data Type: {0}\n".format(self.data_type))
|
||||
self.vf.write("// Data File: {0}\n".format(self.rom_data))
|
||||
|
||||
self.vf.write("\n")
|
||||
|
||||
try:
|
||||
self.vdd_name = spice["power"]
|
||||
except KeyError:
|
||||
self.vdd_name = "vdd"
|
||||
try:
|
||||
self.gnd_name = spice["ground"]
|
||||
except KeyError:
|
||||
self.gnd_name = "gnd"
|
||||
|
||||
#add multiple banks later
|
||||
self.vf.write("module {0}(\n".format(self.name))
|
||||
self.vf.write("`ifdef USE_POWER_PINS\n")
|
||||
self.vf.write(" {},\n".format(self.vdd_name))
|
||||
self.vf.write(" {},\n".format(self.gnd_name))
|
||||
self.vf.write("`endif\n")
|
||||
|
||||
for port in self.all_ports:
|
||||
if port in self.read_ports:
|
||||
self.vf.write("// Port {0}: R\n".format(port))
|
||||
self.vf.write(" clk{0},cs{0},addr{0},dout{0}".format(port))
|
||||
# Continue for every port on a new line
|
||||
if port != self.all_ports[-1]:
|
||||
self.vf.write(",\n")
|
||||
self.vf.write("\n );\n\n")
|
||||
|
||||
self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size))
|
||||
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(math.ceil(math.log(self.num_words,2))))
|
||||
self.vf.write(" parameter ROM_DEPTH = 1 << ADDR_WIDTH;\n")
|
||||
self.vf.write(" // FIXME: This delay is arbitrary.\n")
|
||||
self.vf.write(" parameter DELAY = 3 ;\n")
|
||||
self.vf.write(" parameter VERBOSE = 1 ; //Set to 0 to only display warnings\n")
|
||||
self.vf.write(" parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary\n")
|
||||
self.vf.write("\n")
|
||||
|
||||
self.vf.write("`ifdef USE_POWER_PINS\n")
|
||||
self.vf.write(" inout {};\n".format(self.vdd_name))
|
||||
self.vf.write(" inout {};\n".format(self.gnd_name))
|
||||
self.vf.write("`endif\n")
|
||||
|
||||
for port in self.all_ports:
|
||||
self.add_inputs_outputs(port)
|
||||
|
||||
self.vf.write("\n")
|
||||
|
||||
# This is the memory array itself
|
||||
self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:ROM_DEPTH-1];\n\n")
|
||||
|
||||
#write memory init here
|
||||
self.vf.write(f" initial begin\n")
|
||||
if self.data_type == "bin":
|
||||
self.vf.write(f" $readmemb(\"{self.rom_data}\",mem,0,ROM_DEPTH-1);\n")
|
||||
elif self.data_type == "hex":
|
||||
self.vf.write(f" $readmemh(\"{self.rom_data}\",mem,0, ROM_DEPTH-1);\n")
|
||||
else:
|
||||
raise ValueError(f"Data type: {self.data_type} is not supported!")
|
||||
self.vf.write(f" end\n\n")
|
||||
|
||||
for port in self.all_ports:
|
||||
self.register_inputs(port)
|
||||
|
||||
for port in self.all_ports:
|
||||
if port in self.read_ports:
|
||||
self.add_read_block(port)
|
||||
|
||||
self.vf.write("\n")
|
||||
self.vf.write("endmodule\n")
|
||||
self.vf.close()
|
||||
|
||||
def register_inputs(self, port):
|
||||
"""
|
||||
Register the control signal, address and data inputs.
|
||||
"""
|
||||
self.add_regs(port)
|
||||
self.add_flops(port)
|
||||
|
||||
def add_regs(self, port):
|
||||
"""
|
||||
Create the input regs for the given port.
|
||||
"""
|
||||
self.vf.write(" reg cs{0}_reg;\n".format(port))
|
||||
self.vf.write(" reg [ADDR_WIDTH-1:0] addr{0}_reg;\n".format(port))
|
||||
if port in self.read_ports:
|
||||
self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port))
|
||||
|
||||
|
||||
def add_flops(self, port):
|
||||
"""
|
||||
Add the flop behavior logic for a port.
|
||||
"""
|
||||
self.vf.write("\n")
|
||||
self.vf.write(" // All inputs are registers\n")
|
||||
self.vf.write(" always @(posedge clk{0})\n".format(port))
|
||||
self.vf.write(" begin\n")
|
||||
self.vf.write(" cs{0}_reg = cs{0};\n".format(port))
|
||||
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
|
||||
if port in self.read_ports:
|
||||
self.add_write_read_checks(port)
|
||||
|
||||
if port in self.read_ports:
|
||||
self.vf.write(" #(T_HOLD) dout{0} = {1}'bx;\n".format(port, self.word_size))
|
||||
self.vf.write(" if ( cs{0}_reg && VERBOSE ) \n".format(port))
|
||||
self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port))
|
||||
|
||||
self.vf.write(" end\n\n")
|
||||
|
||||
def add_inputs_outputs(self, port):
|
||||
"""
|
||||
Add the module input and output declaration for a port.
|
||||
"""
|
||||
self.vf.write(" input clk{0}; // clock\n".format(port))
|
||||
self.vf.write(" input cs{0}; // active high chip select\n".format(port))
|
||||
|
||||
self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port))
|
||||
if port in self.read_ports:
|
||||
self.vf.write(" output [DATA_WIDTH-1:0] dout{0};\n".format(port))
|
||||
|
||||
def add_write_block(self, port):
|
||||
"""
|
||||
ROM does not take writes thus this function does nothing
|
||||
"""
|
||||
self.vf.write("\n")
|
||||
def add_read_block(self, port):
|
||||
"""
|
||||
Add a read port block.
|
||||
"""
|
||||
self.vf.write("\n")
|
||||
self.vf.write(" // Memory Read Block Port {0}\n".format(port))
|
||||
self.vf.write(" // Read Operation : When cs{0} = 1\n".format(port))
|
||||
self.vf.write(" always @ (negedge clk{0})\n".format(port))
|
||||
self.vf.write(" begin : MEM_READ{0}\n".format(port))
|
||||
self.vf.write(" if (cs{0}_reg)\n".format(port))
|
||||
self.vf.write(" dout{0} <= #(DELAY) mem[addr{0}_reg];\n".format(port))
|
||||
self.vf.write(" end\n")
|
||||
|
||||
def add_write_read_checks(self, rport):
|
||||
"""
|
||||
Since ROMs dont have write ports this does nothing
|
||||
"""
|
||||
pass
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
import copy
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -18,6 +18,7 @@ class wire_spice_model():
|
|||
def cal_wire_c(self, wire_length, wire_width):
|
||||
from openram.tech import spice
|
||||
# Convert the F/um^2 to fF/um^2 then multiple by width and length
|
||||
# FIXME: shouldn't it be 1e15?
|
||||
total_c = (spice["wire_unit_c"]*1e12) * wire_length * wire_width
|
||||
wire_c = total_c / self.lump_num
|
||||
return wire_c
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -19,11 +19,12 @@ from .simulation import *
|
|||
from .measurements import *
|
||||
from .model_check import *
|
||||
from .analytical_util import *
|
||||
from .fake_sram import *
|
||||
|
||||
debug.info(1, "Initializing characterizer...")
|
||||
OPTS.spice_exe = ""
|
||||
|
||||
if not OPTS.analytical_delay:
|
||||
if not OPTS.analytical_delay or OPTS.top_process in ["memfunc", "memchar"]:
|
||||
if OPTS.spice_name:
|
||||
# Capitalize Xyce
|
||||
if OPTS.spice_name == "xyce":
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import os
|
||||
import re
|
||||
from enum import Enum
|
||||
from openram import debug
|
||||
from openram import OPTS
|
||||
|
||||
|
|
@ -107,3 +108,33 @@ def check_dict_values_is_float(dict):
|
|||
if type(value)!=float:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def bidir_search(func, upper, lower, time_out=9):
|
||||
"""
|
||||
Performs bidirectional search over given function with given
|
||||
upper and lower bounds.
|
||||
"""
|
||||
time_count = 0
|
||||
while time_count < time_out:
|
||||
val = (upper + lower) / 2
|
||||
if func(val):
|
||||
return (True, val)
|
||||
time_count += 1
|
||||
return (False, 0)
|
||||
|
||||
|
||||
class bit_polarity(Enum):
|
||||
NONINVERTING = 0
|
||||
INVERTING = 1
|
||||
|
||||
|
||||
class sram_op(Enum):
|
||||
READ_ZERO = 0
|
||||
READ_ONE = 1
|
||||
WRITE_ZERO = 2
|
||||
WRITE_ONE = 3
|
||||
DISABLED_READ_ZERO = 4
|
||||
DISABLED_READ_ONE = 5
|
||||
DISABLED_WRITE_ZERO = 6
|
||||
DISABLED_WRITE_ONE = 7
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -13,10 +13,10 @@ from openram import OPTS
|
|||
from .stimuli import *
|
||||
from .trim_spice import *
|
||||
from .charutils import *
|
||||
from .sram_op import *
|
||||
from .bit_polarity import *
|
||||
from .simulation import simulation
|
||||
from .measurements import *
|
||||
from os import path
|
||||
import re
|
||||
|
||||
|
||||
class delay(simulation):
|
||||
|
|
@ -37,7 +37,7 @@ class delay(simulation):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, sram, spfile, corner):
|
||||
def __init__(self, sram, spfile, corner, output_path=None):
|
||||
super().__init__(sram, spfile, corner)
|
||||
|
||||
self.targ_read_ports = []
|
||||
|
|
@ -47,10 +47,17 @@ class delay(simulation):
|
|||
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
if output_path is None:
|
||||
self.output_path = OPTS.openram_temp
|
||||
else:
|
||||
self.output_path = output_path
|
||||
|
||||
self.set_load_slew(0, 0)
|
||||
self.set_corner(corner)
|
||||
self.create_signal_names()
|
||||
self.add_graph_exclusions()
|
||||
self.meas_id = 0
|
||||
|
||||
def create_measurement_objects(self):
|
||||
""" Create the measurements used for read and write ports """
|
||||
|
|
@ -79,10 +86,12 @@ class delay(simulation):
|
|||
self.clk_frmt = "clk{0}" # Unformatted clock name
|
||||
targ_name = "{0}{{}}_{1}".format(self.dout_name, self.probe_data) # Empty values are the port and probe data bit
|
||||
self.delay_meas = []
|
||||
self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9))
|
||||
self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "FALL", "RISE", measure_scale=1e9))
|
||||
self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file.
|
||||
self.delay_meas[-1].meta_add_delay = False
|
||||
self.delay_meas.append(delay_measure("delay_hl", self.clk_frmt, targ_name, "FALL", "FALL", measure_scale=1e9))
|
||||
self.delay_meas[-1].meta_str = sram_op.READ_ZERO
|
||||
self.delay_meas[-1].meta_add_delay = False
|
||||
self.read_lib_meas+=self.delay_meas
|
||||
|
||||
self.slew_meas = []
|
||||
|
|
@ -103,9 +112,10 @@ class delay(simulation):
|
|||
self.read_lib_meas[-1].meta_str = "disabled_read0"
|
||||
|
||||
# This will later add a half-period to the spice time delay. Only for reading 0.
|
||||
for obj in self.read_lib_meas:
|
||||
if obj.meta_str is sram_op.READ_ZERO:
|
||||
obj.meta_add_delay = True
|
||||
# FIXME: Removed this to check, see if it affects anything
|
||||
#for obj in self.read_lib_meas:
|
||||
# if obj.meta_str is sram_op.READ_ZERO:
|
||||
# obj.meta_add_delay = True
|
||||
|
||||
read_measures = []
|
||||
read_measures.append(self.read_lib_meas)
|
||||
|
|
@ -113,7 +123,9 @@ class delay(simulation):
|
|||
read_measures.append(self.create_bitline_measurement_objects())
|
||||
read_measures.append(self.create_debug_measurement_objects())
|
||||
read_measures.append(self.create_read_bit_measures())
|
||||
read_measures.append(self.create_sen_and_bitline_path_measures())
|
||||
# TODO: Maybe don't do this here (?)
|
||||
if OPTS.top_process != "memchar":
|
||||
read_measures.append(self.create_sen_and_bitline_path_measures())
|
||||
|
||||
return read_measures
|
||||
|
||||
|
|
@ -158,6 +170,7 @@ class delay(simulation):
|
|||
write_measures = []
|
||||
write_measures.append(self.write_lib_meas)
|
||||
write_measures.append(self.create_write_bit_measures())
|
||||
|
||||
return write_measures
|
||||
|
||||
def create_debug_measurement_objects(self):
|
||||
|
|
@ -216,8 +229,12 @@ class delay(simulation):
|
|||
|
||||
bit_col = self.get_data_bit_column_number(probe_address, probe_data)
|
||||
bit_row = self.get_address_row_number(probe_address)
|
||||
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col)
|
||||
storage_names = cell_inst.mod.get_storage_net_names()
|
||||
if OPTS.top_process == "memchar":
|
||||
cell_name = self.cell_name.format(bit_row, bit_col)
|
||||
storage_names = ("Q", "Q_bar")
|
||||
else:
|
||||
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col)
|
||||
storage_names = cell_inst.mod.get_storage_net_names()
|
||||
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
|
||||
"supported for characterization. Storage nets={0}").format(storage_names))
|
||||
if OPTS.use_pex and OPTS.pex_exe[0] != "calibre":
|
||||
|
|
@ -239,8 +256,8 @@ class delay(simulation):
|
|||
def create_sen_and_bitline_path_measures(self):
|
||||
"""Create measurements for the s_en and bitline paths for individual delays per stage."""
|
||||
|
||||
# FIXME: There should be a default_read_port variable in this case, pathing is done with this
|
||||
# but is never mentioned otherwise
|
||||
# # FIXME: There should be a default_read_port variable in this case, pathing is done with this
|
||||
# # but is never mentioned otherwise
|
||||
port = self.read_ports[0]
|
||||
sen_and_port = self.sen_name + str(port)
|
||||
bl_and_port = self.bl_name.format(port) # bl_name contains a '{}' for the port
|
||||
|
|
@ -255,8 +272,8 @@ class delay(simulation):
|
|||
bitline_path = bl_paths[0]
|
||||
|
||||
# Get the measures
|
||||
self.sen_path_meas = self.create_delay_path_measures(sen_path)
|
||||
self.bl_path_meas = self.create_delay_path_measures(bitline_path)
|
||||
self.sen_path_meas = self.create_delay_path_measures(sen_path, "sen")
|
||||
self.bl_path_meas = self.create_delay_path_measures(bitline_path, "bl")
|
||||
all_meas = self.sen_path_meas + self.bl_path_meas
|
||||
|
||||
# Paths could have duplicate measurements, remove them before they go to the stim file
|
||||
|
|
@ -278,7 +295,7 @@ class delay(simulation):
|
|||
|
||||
return unique_measures
|
||||
|
||||
def create_delay_path_measures(self, path):
|
||||
def create_delay_path_measures(self, path, process):
|
||||
"""Creates measurements for each net along given path."""
|
||||
|
||||
# Determine the directions (RISE/FALL) of signals
|
||||
|
|
@ -290,6 +307,8 @@ class delay(simulation):
|
|||
cur_net, next_net = path[i], path[i + 1]
|
||||
cur_dir, next_dir = path_dirs[i], path_dirs[i + 1]
|
||||
meas_name = "delay_{0}_to_{1}".format(cur_net, next_net)
|
||||
meas_name += "_" + process + "_id" + str(self.meas_id)
|
||||
self.meas_id += 1
|
||||
if i + 1 != len(path) - 1:
|
||||
path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, next_dir, measure_scale=1e9, has_port=False))
|
||||
else: # Make the last measurement always measure on FALL because is a read 0
|
||||
|
|
@ -368,7 +387,7 @@ class delay(simulation):
|
|||
|
||||
self.sf.write("\n* SRAM output loads\n")
|
||||
for port in self.read_ports:
|
||||
for i in range(self.word_size):
|
||||
for i in range(self.word_size + self.num_spare_cols):
|
||||
self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port, i, self.dout_name, self.load))
|
||||
|
||||
def write_delay_stimulus(self):
|
||||
|
|
@ -385,15 +404,20 @@ class delay(simulation):
|
|||
|
||||
# creates and opens stimulus file for writing
|
||||
self.delay_stim_sp = "delay_stim.sp"
|
||||
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.delay_stim_sp)
|
||||
temp_stim = path.join(self.output_path, self.delay_stim_sp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
|
||||
# creates and opens measure file for writing
|
||||
self.delay_meas_sp = "delay_meas.sp"
|
||||
temp_meas = path.join(self.output_path, self.delay_meas_sp)
|
||||
self.mf = open(temp_meas, "w")
|
||||
|
||||
if OPTS.spice_name == "spectre":
|
||||
self.sf.write("simulator lang=spice\n")
|
||||
self.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period,
|
||||
self.load,
|
||||
self.slew))
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
self.stim = stimuli(self.sf, self.mf, self.corner)
|
||||
# include files in stimulus file
|
||||
self.stim.write_include(self.trim_sp_file)
|
||||
|
||||
|
|
@ -418,6 +442,7 @@ class delay(simulation):
|
|||
t_rise=self.slew,
|
||||
t_fall=self.slew)
|
||||
|
||||
self.sf.write(".include {0}".format(temp_meas))
|
||||
# self.load_all_measure_nets()
|
||||
self.write_delay_measures()
|
||||
# self.write_simulation_saves()
|
||||
|
|
@ -426,6 +451,7 @@ class delay(simulation):
|
|||
self.stim.write_control(self.cycle_times[-1] + self.period)
|
||||
|
||||
self.sf.close()
|
||||
self.mf.close()
|
||||
|
||||
def write_power_stimulus(self, trim):
|
||||
""" Creates a stimulus file to measure leakage power only.
|
||||
|
|
@ -435,10 +461,15 @@ class delay(simulation):
|
|||
|
||||
# creates and opens stimulus file for writing
|
||||
self.power_stim_sp = "power_stim.sp"
|
||||
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.power_stim_sp)
|
||||
temp_stim = path.join(self.output_path, self.power_stim_sp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
|
||||
# creates and opens measure file for writing
|
||||
self.power_meas_sp = "power_meas.sp"
|
||||
temp_meas = path.join(self.output_path, self.power_meas_sp)
|
||||
self.mf = open(temp_meas, "w")
|
||||
self.stim = stimuli(self.sf, self.mf, self.corner)
|
||||
|
||||
# include UNTRIMMED files in stimulus file
|
||||
if trim:
|
||||
|
|
@ -451,7 +482,7 @@ class delay(simulation):
|
|||
# generate data and addr signals
|
||||
self.sf.write("\n* Generation of data and address signals\n")
|
||||
for write_port in self.write_ports:
|
||||
for i in range(self.word_size):
|
||||
for i in range(self.word_size + self.num_spare_cols):
|
||||
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i),
|
||||
v_val=0)
|
||||
for port in self.all_ports:
|
||||
|
|
@ -470,12 +501,14 @@ class delay(simulation):
|
|||
for port in self.all_ports:
|
||||
self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0)
|
||||
|
||||
self.sf.write(".include {}".format(temp_meas))
|
||||
self.write_power_measures()
|
||||
|
||||
# run until the end of the cycle time
|
||||
self.stim.write_control(2 * self.period)
|
||||
|
||||
self.sf.close()
|
||||
self.mf.close()
|
||||
|
||||
def get_measure_variants(self, port, measure_obj, measure_type=None):
|
||||
"""
|
||||
|
|
@ -587,15 +620,15 @@ class delay(simulation):
|
|||
# Output some comments to aid where cycles start and
|
||||
# what is happening
|
||||
for comment in self.cycle_comments:
|
||||
self.sf.write("* {0}\n".format(comment))
|
||||
self.mf.write("* {0}\n".format(comment))
|
||||
|
||||
self.sf.write("\n")
|
||||
for read_port in self.targ_read_ports:
|
||||
self.sf.write("* Read ports {0}\n".format(read_port))
|
||||
self.mf.write("* Read ports {0}\n".format(read_port))
|
||||
self.write_delay_measures_read_port(read_port)
|
||||
|
||||
for write_port in self.targ_write_ports:
|
||||
self.sf.write("* Write ports {0}\n".format(write_port))
|
||||
self.mf.write("* Write ports {0}\n".format(write_port))
|
||||
self.write_delay_measures_write_port(write_port)
|
||||
|
||||
def load_pex_net(self, net: str):
|
||||
|
|
@ -605,8 +638,8 @@ class delay(simulation):
|
|||
return net
|
||||
original_net = net
|
||||
net = net[len(prefix):]
|
||||
net = net.replace(".", "_").replace("[", "\[").replace("]", "\]")
|
||||
for pattern in ["\sN_{}_[MXmx]\S+_[gsd]".format(net), net]:
|
||||
net = net.replace(".", "_").replace("[", r"\[").replace("]", r"\]")
|
||||
for pattern in [r"\sN_{}_[MXmx]\S+_[gsd]".format(net), net]:
|
||||
try:
|
||||
match = check_output(["grep", "-m1", "-o", "-iE", pattern, self.sp_file])
|
||||
return prefix + match.decode().strip()
|
||||
|
|
@ -616,8 +649,8 @@ class delay(simulation):
|
|||
|
||||
def load_all_measure_nets(self):
|
||||
measurement_nets = set()
|
||||
for port, meas in zip(self.targ_read_ports * len(self.read_meas_lists) +
|
||||
self.targ_write_ports * len(self.write_meas_lists),
|
||||
for port, meas in zip(self.targ_read_ports * len(self.read_meas_lists)
|
||||
+ self.targ_write_ports * len(self.write_meas_lists),
|
||||
self.read_meas_lists + self.write_meas_lists):
|
||||
for measurement in meas:
|
||||
visited = getattr(measurement, 'pex_visited', False)
|
||||
|
|
@ -684,6 +717,8 @@ class delay(simulation):
|
|||
self.sf.write("\n* Measure statements for idle leakage power\n")
|
||||
|
||||
# add measure statements for power
|
||||
# TODO: Convert to measure statement insted of using stimuli
|
||||
# measure = power_measure('leakage_power',
|
||||
t_initial = self.period
|
||||
t_final = 2 * self.period
|
||||
self.stim.gen_meas_power(meas_name="leakage_power",
|
||||
|
|
@ -791,7 +826,7 @@ class delay(simulation):
|
|||
|
||||
for port in self.targ_write_ports:
|
||||
if not self.check_bit_measures(self.write_bit_meas, port):
|
||||
return(False, {})
|
||||
return (False, {})
|
||||
|
||||
debug.info(2, "Checking write values for port {0}".format(port))
|
||||
write_port_dict = {}
|
||||
|
|
@ -805,7 +840,7 @@ class delay(simulation):
|
|||
for port in self.targ_read_ports:
|
||||
# First, check that the memory has the right values at the right times
|
||||
if not self.check_bit_measures(self.read_bit_meas, port):
|
||||
return(False, {})
|
||||
return (False, {})
|
||||
|
||||
debug.info(2, "Checking read delay values for port {0}".format(port))
|
||||
# Check sen timing, then bitlines, then general measurements.
|
||||
|
|
@ -828,7 +863,8 @@ class delay(simulation):
|
|||
|
||||
result[port].update(read_port_dict)
|
||||
|
||||
self.path_delays = self.check_path_measures()
|
||||
if self.sen_path_meas and self.bl_path_meas:
|
||||
self.path_delays = self.check_path_measures()
|
||||
|
||||
return (True, result)
|
||||
|
||||
|
|
@ -919,7 +955,7 @@ class delay(simulation):
|
|||
def check_bitline_meas(self, v_discharged_bl, v_charged_bl):
|
||||
"""
|
||||
Checks the value of the discharging bitline. Confirms s_en timing errors.
|
||||
Returns true if the bitlines are at there expected value.
|
||||
Returns true if the bitlines are at there their value.
|
||||
"""
|
||||
# The inputs looks at discharge/charged bitline rather than left or right (bl/br)
|
||||
# Performs two checks, discharging bitline is at least 10% away from vdd and there is a
|
||||
|
|
@ -942,7 +978,7 @@ class delay(simulation):
|
|||
if type(val) != float or val > self.period / 2:
|
||||
debug.info(1, 'Failed measurement:{}={}'.format(meas.name, val))
|
||||
value_dict[meas.name] = val
|
||||
#debug.info(0, "value_dict={}".format(value_dict))
|
||||
# debug.info(0, "value_dict={}".format(value_dict))
|
||||
return value_dict
|
||||
|
||||
def run_power_simulation(self):
|
||||
|
|
@ -992,8 +1028,8 @@ class delay(simulation):
|
|||
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh)
|
||||
# high-to-low delays start at neg. clk edge, so they need to be less than half_period
|
||||
half_period = self.period / 2
|
||||
if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \
|
||||
or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0:
|
||||
if abs(delay_hl)>half_period or abs(delay_lh)>half_period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \
|
||||
or (delay_hl<0 and delay_lh<0) or slew_hl<0 or slew_lh<0:
|
||||
debug.info(2, "UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
|
||||
delays_str,
|
||||
slews_str))
|
||||
|
|
@ -1003,6 +1039,13 @@ class delay(simulation):
|
|||
delays_str,
|
||||
slews_str))
|
||||
|
||||
if delay_lh < 0 and delay_hl > 0:
|
||||
result_dict["delay_lh"] = result_dict["delay_hl"]
|
||||
debug.info(2, "delay_lh captured precharge, using delay_hl instead")
|
||||
elif delay_hl < 0 and delay_lh > 0:
|
||||
result_dict["delay_hl"] = result_dict["delay_lh"]
|
||||
debug.info(2, "delay_hl captured precharge, using delay_lh instead")
|
||||
|
||||
return True
|
||||
|
||||
def find_min_period(self, feasible_delays):
|
||||
|
|
@ -1108,32 +1151,143 @@ class delay(simulation):
|
|||
Netlist reduced for simulation.
|
||||
"""
|
||||
super().set_probe(probe_address, probe_data)
|
||||
self.prepare_netlist()
|
||||
|
||||
def prepare_netlist(self):
|
||||
""" Prepare a trimmed netlist and regular netlist. """
|
||||
|
||||
# Set up to trim the netlist here if that is enabled
|
||||
# TODO: Copy old netlist if memchar
|
||||
if OPTS.trim_netlist:
|
||||
#self.trim_sp_file = "{0}trimmed.sp".format(self.output_path)
|
||||
self.trim_sp_file = "{0}trimmed.sp".format(OPTS.openram_temp)
|
||||
self.sram.sp_write(self.trim_sp_file, lvs=False, trim=True)
|
||||
# Only genrate spice when running openram process
|
||||
if OPTS.top_process != "memchar":
|
||||
self.sram.sp_write(self.trim_sp_file, lvs=False, trim=True)
|
||||
else:
|
||||
# The non-reduced netlist file when it is disabled
|
||||
self.trim_sp_file = "{0}sram.sp".format(OPTS.openram_temp)
|
||||
self.trim_sp_file = "{0}sram.sp".format(self.output_path)
|
||||
|
||||
# The non-reduced netlist file for power simulation
|
||||
self.sim_sp_file = "{0}sram.sp".format(OPTS.openram_temp)
|
||||
self.sim_sp_file = "{0}sram.sp".format(self.output_path)
|
||||
# Make a copy in temp for debugging
|
||||
shutil.copy(self.sp_file, self.sim_sp_file)
|
||||
if self.sp_file != self.sim_sp_file:
|
||||
shutil.copy(self.sp_file, self.sim_sp_file)
|
||||
|
||||
def recover_measurment_objects(self):
|
||||
mf_path = path.join(OPTS.output_path, "delay_meas.sp")
|
||||
self.sen_path_meas = None
|
||||
self.bl_path_meas = None
|
||||
if not path.exists(mf_path):
|
||||
debug.info(1, "Delay measure file not found. Skipping measure recovery")
|
||||
return
|
||||
mf = open(mf_path, "r")
|
||||
measure_text = mf.read()
|
||||
port_iter = re.finditer(r"\* (Read|Write) ports (\d*)", measure_text)
|
||||
port_measure_lines = []
|
||||
loc = 0
|
||||
port_name = ''
|
||||
for port in port_iter:
|
||||
port_measure_lines.append((port_name, measure_text[loc:port.end(0)]))
|
||||
loc = port.start(0)
|
||||
port_name = port.group(1) + port.group(2)
|
||||
|
||||
mf.close()
|
||||
# Cycle comments, not sure if i need this
|
||||
# cycle_lines = port_measure_lines.pop(0)[1]
|
||||
# For now just recover the bit_measures and sen_and_bitline_path_measures
|
||||
self.read_meas_lists.append([])
|
||||
self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
|
||||
self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
|
||||
# bit_measure_rule = re.compile(r"\.meas tran (v_q_a\d+_b\d+_(read|write)_(zero|one)\d+) FIND v\((.*)\) AT=(\d+(\.\d+)?)n")
|
||||
# for measures in port_measure_lines:
|
||||
# port_name = measures[0]
|
||||
# text = measures[1]
|
||||
# bit_measure_iter = bit_measure_rule.finditer(text)
|
||||
# for bit_measure in bit_measure_iter:
|
||||
# meas_name = bit_measure.group(1)
|
||||
# read = bit_measure.group(2) == "read"
|
||||
# cycle = bit_measure.group(3)
|
||||
# probe = bit_measure.group(4)
|
||||
# polarity = bit_polarity.NONINVERTING
|
||||
# if "q_bar" in meas_name:
|
||||
# polarity = bit_polarity.INVERTING
|
||||
# meas = voltage_at_measure(meas_name, probe)
|
||||
# if read:
|
||||
# if cycle == "one":
|
||||
# meas.meta_str = sram_op.READ_ONE
|
||||
# else:
|
||||
# meas.meta_str = sram_op.READ_ZERO
|
||||
# self.read_bit_meas[polarity].append(meas)
|
||||
# self.read_meas_lists[-1].append(meas)
|
||||
# else:
|
||||
# if cycle == "one":
|
||||
# meas.meta_str = sram_op.WRITE_ONE
|
||||
# else:
|
||||
# meas.meta_str = sram_op.WRITE_ZERO
|
||||
# self.write_bit_meas[polarity].append(meas)
|
||||
# self.write_meas_lists[-1].append(meas)
|
||||
|
||||
delay_path_rule = re.compile(r"\.meas tran delay_(.*)_to_(.*)_(sen|bl)_(id\d*) TRIG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n TARG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n")
|
||||
port = self.read_ports[0]
|
||||
meas_buff = []
|
||||
self.sen_path_meas = []
|
||||
self.bl_path_meas = []
|
||||
for measures in port_measure_lines:
|
||||
text = measures[1]
|
||||
delay_path_iter = delay_path_rule.finditer(text)
|
||||
for delay_path_measure in delay_path_iter:
|
||||
from_ = delay_path_measure.group(1)
|
||||
to_ = delay_path_measure.group(2)
|
||||
path_ = delay_path_measure.group(3)
|
||||
id_ = delay_path_measure.group(4)
|
||||
trig_rise = delay_path_measure.group(8)
|
||||
targ_rise = delay_path_measure.group(15)
|
||||
meas_name = "delay_{0}_to_{1}_{2}_{3}".format(from_, to_, path_, id_)
|
||||
meas = delay_measure(meas_name, from_, to_, trig_rise, targ_rise, measure_scale=1e9, has_port=False)
|
||||
meas.meta_str = sram_op.READ_ZERO
|
||||
meas.meta_add_delay = True
|
||||
meas_buff.append(meas)
|
||||
if path_ == "sen":
|
||||
self.sen_path_meas.extend(meas_buff.copy())
|
||||
meas_buff.clear()
|
||||
elif path_ == "bl":
|
||||
self.bl_path_meas.extend(meas_buff.copy())
|
||||
meas_buff.clear()
|
||||
self.read_meas_lists.append(self.sen_path_meas + self.bl_path_meas)
|
||||
|
||||
def set_spice_names(self):
|
||||
"""This is run in place of set_internal_spice_names function from
|
||||
simulation.py when running stand-alone characterizer."""
|
||||
self.bl_name = OPTS.bl_format.format(name=self.sram.name,
|
||||
hier_sep=OPTS.hier_seperator,
|
||||
row="{}",
|
||||
col=self.bitline_column)
|
||||
self.br_name = OPTS.br_format.format(name=self.sram.name,
|
||||
hier_sep=OPTS.hier_seperator,
|
||||
row="{}",
|
||||
col=self.bitline_column)
|
||||
self.sen_name = OPTS.sen_format.format(name=self.sram.name,
|
||||
hier_sep=OPTS.hier_seperator)
|
||||
self.cell_name = OPTS.cell_format.format(name=self.sram.name,
|
||||
hier_sep=OPTS.hier_seperator,
|
||||
row="{}",
|
||||
col="{}")
|
||||
|
||||
def analysis_init(self, probe_address, probe_data):
|
||||
"""Sets values which are dependent on the data address/bit being tested."""
|
||||
|
||||
self.set_probe(probe_address, probe_data)
|
||||
self.create_graph()
|
||||
self.set_internal_spice_names()
|
||||
self.create_measurement_names()
|
||||
self.create_measurement_objects()
|
||||
self.prepare_netlist()
|
||||
if OPTS.top_process == "memchar":
|
||||
self.set_spice_names()
|
||||
self.create_measurement_names()
|
||||
self.create_measurement_objects()
|
||||
self.recover_measurment_objects()
|
||||
else:
|
||||
self.create_graph()
|
||||
self.set_internal_spice_names()
|
||||
self.create_measurement_names()
|
||||
self.create_measurement_objects()
|
||||
|
||||
def analyze(self, probe_address, probe_data, load_slews):
|
||||
"""
|
||||
|
|
@ -1145,7 +1299,7 @@ class delay(simulation):
|
|||
self.analysis_init(probe_address, probe_data)
|
||||
loads = []
|
||||
slews = []
|
||||
for load,slew in load_slews:
|
||||
for load, slew in load_slews:
|
||||
loads.append(load)
|
||||
slews.append(slew)
|
||||
self.load=max(loads)
|
||||
|
|
@ -1168,15 +1322,16 @@ class delay(simulation):
|
|||
# 4) At the minimum period, measure the delay, slew and power for all slew/load pairs.
|
||||
self.period = min_period
|
||||
char_port_data = self.simulate_loads_and_slews(load_slews, leakage_offset)
|
||||
if OPTS.use_specified_load_slew != None and len(load_slews) > 1:
|
||||
if OPTS.use_specified_load_slew is not None and len(load_slews) > 1:
|
||||
debug.warning("Path delay lists not correctly generated for characterizations of more than 1 load,slew")
|
||||
# Get and save the path delays
|
||||
bl_names, bl_delays, sen_names, sen_delays = self.get_delay_lists(self.path_delays)
|
||||
if self.sen_path_meas and self.bl_path_meas:
|
||||
bl_names, bl_delays, sen_names, sen_delays = self.get_delay_lists(self.path_delays)
|
||||
# Removed from characterization output temporarily
|
||||
#char_sram_data["bl_path_measures"] = bl_delays
|
||||
#char_sram_data["sen_path_measures"] = sen_delays
|
||||
#char_sram_data["bl_path_names"] = bl_names
|
||||
#char_sram_data["sen_path_names"] = sen_names
|
||||
# char_sram_data["bl_path_measures"] = bl_delays
|
||||
# char_sram_data["sen_path_measures"] = sen_delays
|
||||
# char_sram_data["bl_path_names"] = bl_names
|
||||
# char_sram_data["sen_path_names"] = sen_names
|
||||
# FIXME: low-to-high delays are altered to be independent of the period. This makes the lib results less accurate.
|
||||
self.alter_lh_char_data(char_port_data)
|
||||
|
||||
|
|
@ -1185,7 +1340,7 @@ class delay(simulation):
|
|||
def alter_lh_char_data(self, char_port_data):
|
||||
"""Copies high-to-low data to low-to-high data to make them consistent on the same clock edge."""
|
||||
|
||||
# This is basically a hack solution which should be removed/fixed later.
|
||||
# This is basically a hack solution which should be removed/fixed later.
|
||||
for port in self.all_ports:
|
||||
char_port_data[port]['delay_lh'] = char_port_data[port]['delay_hl']
|
||||
char_port_data[port]['slew_lh'] = char_port_data[port]['slew_hl']
|
||||
|
|
@ -1194,7 +1349,6 @@ class delay(simulation):
|
|||
"""Simulate all specified output loads and input slews pairs of all ports"""
|
||||
|
||||
measure_data = self.get_empty_measure_data_dict()
|
||||
path_dict = {}
|
||||
# Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways.
|
||||
self.targ_read_ports = self.read_ports
|
||||
self.targ_write_ports = self.write_ports
|
||||
|
|
@ -1255,8 +1409,8 @@ class delay(simulation):
|
|||
inverse_address = self.calculate_inverse_address()
|
||||
|
||||
# For now, ignore data patterns and write ones or zeros
|
||||
data_ones = "1" * self.word_size
|
||||
data_zeros = "0" * self.word_size
|
||||
data_ones = "1" * (self.word_size + self.num_spare_cols)
|
||||
data_zeros = "0" * (self.word_size + self.num_spare_cols)
|
||||
wmask_ones = "1" * self.num_wmasks
|
||||
|
||||
if self.t_current == 0:
|
||||
|
|
@ -1352,9 +1506,9 @@ class delay(simulation):
|
|||
# Get any available read/write port in case only a single write or read ports is being characterized.
|
||||
cur_read_port = self.get_available_port(get_read_port=True)
|
||||
cur_write_port = self.get_available_port(get_read_port=False)
|
||||
debug.check(cur_read_port != None,
|
||||
debug.check(cur_read_port is not None,
|
||||
"Characterizer requires at least 1 read port")
|
||||
debug.check(cur_write_port != None,
|
||||
debug.check(cur_write_port is not None,
|
||||
"Characterizer requires at least 1 write port")
|
||||
|
||||
# Create test cycles for specified target ports.
|
||||
|
|
@ -1380,7 +1534,7 @@ class delay(simulation):
|
|||
""" Generates the PWL data inputs for a simulation timing test. """
|
||||
|
||||
for write_port in self.write_ports:
|
||||
for i in range(self.word_size):
|
||||
for i in range(self.word_size + self.num_spare_cols):
|
||||
sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i)
|
||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05)
|
||||
|
||||
|
|
@ -1402,3 +1556,6 @@ class delay(simulation):
|
|||
self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
|
||||
if port in self.readwrite_ports:
|
||||
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
|
||||
if self.sram.num_wmasks:
|
||||
for bit in range(self.sram.num_wmasks):
|
||||
self.stim.gen_pwl("WMASK{0}_{1}".format(port, bit), self.cycle_times, self.wmask_values[port][bit], self.period, self.slew, 0.05)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram import sram_config
|
||||
from math import ceil
|
||||
from openram import OPTS
|
||||
|
||||
|
||||
class fake_sram(sram_config):
|
||||
"""
|
||||
This is an SRAM class that doesn't actually create an instance.
|
||||
It will read neccessary members from HTML file from a previous run.
|
||||
"""
|
||||
def __init__(self, name, word_size, num_words, write_size=None, num_banks=1,
|
||||
words_per_row=None, num_spare_rows=0, num_spare_cols=0):
|
||||
sram_config.__init__(self, word_size, num_words, write_size,
|
||||
num_banks, words_per_row, num_spare_rows,
|
||||
num_spare_cols)
|
||||
self.name = name
|
||||
if write_size and self.write_size != self.word_size:
|
||||
self.num_wmasks = int(ceil(self.word_size / self.write_size))
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
def setup_multiport_constants(self):
|
||||
"""
|
||||
Taken from ../base/design.py
|
||||
These are contants and lists that aid multiport design.
|
||||
Ports are always in the order RW, W, R.
|
||||
Port indices start from 0 and increment.
|
||||
A first RW port will have clk0, csb0, web0, addr0, data0
|
||||
A first W port (with no RW ports) will be: clk0, csb0, addr0, data0
|
||||
|
||||
"""
|
||||
total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
|
||||
|
||||
# These are the read/write port indices.
|
||||
self.readwrite_ports = []
|
||||
# These are the read/write and write-only port indices
|
||||
self.write_ports = []
|
||||
# These are the write-only port indices.
|
||||
self.writeonly_ports = []
|
||||
# These are the read/write and read-only port indices
|
||||
self.read_ports = []
|
||||
# These are the read-only port indices.
|
||||
self.readonly_ports = []
|
||||
# These are all the ports
|
||||
self.all_ports = list(range(total_ports))
|
||||
|
||||
# The order is always fixed as RW, W, R
|
||||
port_number = 0
|
||||
for port in range(OPTS.num_rw_ports):
|
||||
self.readwrite_ports.append(port_number)
|
||||
self.write_ports.append(port_number)
|
||||
self.read_ports.append(port_number)
|
||||
port_number += 1
|
||||
for port in range(OPTS.num_w_ports):
|
||||
self.write_ports.append(port_number)
|
||||
self.writeonly_ports.append(port_number)
|
||||
port_number += 1
|
||||
for port in range(OPTS.num_r_ports):
|
||||
self.read_ports.append(port_number)
|
||||
self.readonly_ports.append(port_number)
|
||||
port_number += 1
|
||||
|
||||
def generate_pins(self):
|
||||
self.pins = ['vdd', 'gnd']
|
||||
self.pins.extend(['clk{}'.format(port) for port in range(
|
||||
OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports)])
|
||||
for port in range(OPTS.num_rw_ports):
|
||||
self.pins.extend(['din{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.word_size + self.num_spare_cols)])
|
||||
self.pins.extend(['dout{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.word_size + self.num_spare_cols)])
|
||||
self.pins.extend(['addr{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.addr_size)])
|
||||
self.pins.extend(['spare_wen{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.num_spare_cols)])
|
||||
if self.num_wmasks != 0:
|
||||
self.pins.extend(['wmask{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.num_wmasks)])
|
||||
|
||||
self.pins.extend(['csb{}'.format(port), 'web{}'.format(port)])
|
||||
|
||||
start_port = OPTS.num_rw_ports
|
||||
for port in range(start_port, start_port + OPTS.num_r_ports):
|
||||
self.pins.extend(['dout{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.word_size + self.num_spare_cols)])
|
||||
self.pins.extend(['addr{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.addr_size)])
|
||||
|
||||
self.pins.extend(['csb{}'.format(port)])
|
||||
|
||||
start_port += OPTS.num_r_ports
|
||||
for port in range(start_port, start_port + OPTS.num_w_ports):
|
||||
self.pins.extend(['din{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.word_size + self.num_spare_cols)])
|
||||
self.pins.extend(['spare_wen{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.num_spare_cols)])
|
||||
self.pins.extend(['addr{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.addr_size)])
|
||||
if self.num_wmasks != 0:
|
||||
self.pins.extend(['wmask{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.num_wmasks)])
|
||||
|
||||
self.pins.extend(['csb{}'.format(port), 'web{}'.format(port)])
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import math
|
||||
import random
|
||||
import time
|
||||
import collections
|
||||
from os import path
|
||||
import shutil
|
||||
from numpy import binary_repr
|
||||
from openram import debug
|
||||
from openram import OPTS
|
||||
from .stimuli import *
|
||||
from .charutils import *
|
||||
from .simulation import simulation
|
||||
from .measurements import voltage_at_measure
|
||||
|
||||
|
||||
class functional(simulation):
|
||||
|
|
@ -28,17 +32,27 @@ class functional(simulation):
|
|||
# Seed the characterizer with a constant seed for unit tests
|
||||
if OPTS.is_unit_test:
|
||||
random.seed(12345)
|
||||
elif OPTS.functional_seed:
|
||||
random.seed(OPTS.functional_seed)
|
||||
else:
|
||||
seed = time.time_ns()
|
||||
random.seed(seed)
|
||||
debug.info(1, "Random seed for functional simulation: {}".format(seed))
|
||||
|
||||
|
||||
if not spfile:
|
||||
# self.sp_file is assigned in base class
|
||||
sram.sp_write(self.sp_file, trim=OPTS.trim_netlist)
|
||||
# Copy sp file to temp dir
|
||||
self.temp_spice = path.join(OPTS.openram_temp, "sram.sp")
|
||||
try:
|
||||
shutil.copy(self.sp_file, self.temp_spice)
|
||||
except shutil.SameFileError: # skip if the same
|
||||
pass
|
||||
|
||||
if not corner:
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
|
||||
if period:
|
||||
self.period = period
|
||||
|
||||
if not output_path:
|
||||
self.output_path = OPTS.openram_temp
|
||||
else:
|
||||
|
|
@ -73,13 +87,20 @@ class functional(simulation):
|
|||
self.set_spice_constants()
|
||||
self.set_stimulus_variables()
|
||||
|
||||
# Override default period
|
||||
if period:
|
||||
self.period = period
|
||||
|
||||
# For the debug signal names
|
||||
self.wordline_row = 0
|
||||
self.bitline_column = 0
|
||||
self.create_signal_names()
|
||||
self.add_graph_exclusions()
|
||||
self.create_graph()
|
||||
self.set_internal_spice_names()
|
||||
#self.add_graph_exclusions()
|
||||
#self.create_graph()
|
||||
#self.set_internal_spice_names()
|
||||
self.bl_name = "xsram:xbank0:bl_0_{}"
|
||||
self.br_name = "xsram:xbank0:br_0_{}"
|
||||
self.sen_name = "xsram:s_en"
|
||||
self.q_name, self.qbar_name = self.get_bit_name()
|
||||
debug.info(2, "q:\t\t{0}".format(self.q_name))
|
||||
debug.info(2, "qbar:\t{0}".format(self.qbar_name))
|
||||
|
|
@ -145,7 +166,6 @@ class functional(simulation):
|
|||
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.bank_addr_size, "0" * self.num_wmasks, 0, self.t_current)
|
||||
self.add_noop_all_ports(comment)
|
||||
|
||||
|
||||
# 1. Write all the write ports 2x to seed a bunch of locations.
|
||||
for i in range(3):
|
||||
for port in self.write_ports:
|
||||
|
|
@ -267,7 +287,7 @@ class functional(simulation):
|
|||
self.read_check.append([word,
|
||||
"{0}{1}".format(self.dout_name, port),
|
||||
self.t_current + self.period,
|
||||
int(self.t_current/self.period)])
|
||||
int(self.t_current / self.period)])
|
||||
|
||||
def read_stim_results(self):
|
||||
# Extract dout values from spice timing.lis
|
||||
|
|
@ -275,7 +295,8 @@ class functional(simulation):
|
|||
sp_read_value = ""
|
||||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
measure_name = "v{0}_{1}ck{2}".format(dout_port.lower(), bit, cycle)
|
||||
value = parse_spice_list("timing", measure_name)
|
||||
# value = parse_spice_list("timing", measure_name)
|
||||
value = self.measures[measure_name].retrieve_measure(port=0)
|
||||
# FIXME: Ignore the spare columns for now
|
||||
if bit >= self.word_size:
|
||||
value = 0
|
||||
|
|
@ -294,10 +315,11 @@ class functional(simulation):
|
|||
self.v_low,
|
||||
self.v_high)
|
||||
except ValueError:
|
||||
error ="FAILED: {0}_{1} value {2} at time {3}n is not a float.".format(dout_port,
|
||||
error ="FAILED: {0}_{1} value {2} at time {3}n is not a float. Measure: {4}".format(dout_port,
|
||||
bit,
|
||||
value,
|
||||
eo_period)
|
||||
eo_period,
|
||||
measure_name)
|
||||
|
||||
return (0, error)
|
||||
self.read_results.append([sp_read_value, dout_port, eo_period, cycle])
|
||||
|
|
@ -318,8 +340,8 @@ class functional(simulation):
|
|||
cycle,
|
||||
self.read_results[i][2],
|
||||
check_name)
|
||||
return(0, error)
|
||||
return(1, "SUCCESS")
|
||||
return (0, error)
|
||||
return (1, "SUCCESS")
|
||||
|
||||
def gen_wmask(self):
|
||||
wmask = ""
|
||||
|
|
@ -347,7 +369,7 @@ class functional(simulation):
|
|||
def gen_data(self):
|
||||
""" Generates a random word to write. """
|
||||
# Don't use 0 or max value
|
||||
random_value = random.randint(1, self.max_data - 1)
|
||||
random_value = random.randint(1, self.max_data)
|
||||
data_bits = binary_repr(random_value, self.word_size)
|
||||
if self.num_spare_cols>0:
|
||||
random_value = random.randint(0, self.max_col_data)
|
||||
|
|
@ -380,13 +402,16 @@ class functional(simulation):
|
|||
def write_functional_stimulus(self):
|
||||
""" Writes SPICE stimulus. """
|
||||
self.stim_sp = "functional_stim.sp"
|
||||
temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp)
|
||||
temp_stim = path.join(self.output_path, self.stim_sp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Functional test stimulus file for {0}ns period\n\n".format(self.period))
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
self.meas_sp = "functional_meas.sp"
|
||||
temp_meas = path.join(self.output_path, self.meas_sp)
|
||||
self.mf = open(temp_meas, "w")
|
||||
self.stim = stimuli(self.sf, self.mf, self.corner)
|
||||
|
||||
# Write include statements
|
||||
self.stim.write_include(self.sp_file)
|
||||
self.stim.write_include(self.temp_spice)
|
||||
|
||||
# Write Vdd/Gnd statements
|
||||
self.sf.write("\n* Global Power Supplies\n")
|
||||
|
|
@ -470,6 +495,7 @@ class functional(simulation):
|
|||
|
||||
# Generate dout value measurements
|
||||
self.sf.write("\n * Generation of dout measurements\n")
|
||||
self.measures = {}
|
||||
|
||||
for (word, dout_port, eo_period, cycle) in self.read_check:
|
||||
t_initial = eo_period
|
||||
|
|
@ -477,28 +503,37 @@ class functional(simulation):
|
|||
num_bits = self.word_size + self.num_spare_cols
|
||||
for bit in range(num_bits):
|
||||
signal_name = "{0}_{1}".format(dout_port, bit)
|
||||
measure_name = "V{0}ck{1}".format(signal_name, cycle)
|
||||
measure_name = "v{0}ck{1}".format(signal_name, cycle)
|
||||
voltage_value = self.stim.get_voltage(word[num_bits - bit - 1])
|
||||
|
||||
self.stim.add_comment("* CHECK {0} {1} = {2} time = {3}".format(signal_name,
|
||||
measure_name,
|
||||
voltage_value,
|
||||
eo_period))
|
||||
self.stim.gen_meas_value(meas_name=measure_name,
|
||||
dout=signal_name,
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
# TODO: Convert to measurement statement instead of stimuli
|
||||
meas = voltage_at_measure(measure_name, signal_name)
|
||||
self.measures[measure_name] = meas
|
||||
meas.write_measure(self.stim, ((t_initial + t_final) / 2, 0))
|
||||
# self.stim.gen_meas_value(meas_name=measure_name,
|
||||
# dout=signal_name,
|
||||
# t_initial=t_initial,
|
||||
# t_final=t_final
|
||||
|
||||
self.sf.write(".include {0}\n".format(temp_meas))
|
||||
self.stim.write_control(self.cycle_times[-1] + self.period)
|
||||
self.sf.close()
|
||||
self.mf.close()
|
||||
|
||||
# FIXME: Similar function to delay.py, refactor this
|
||||
def get_bit_name(self):
|
||||
""" Get a bit cell name """
|
||||
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
|
||||
storage_names = cell_inst.mod.get_storage_net_names()
|
||||
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
|
||||
"supported for characterization. Storage nets={0}").format(storage_names))
|
||||
# TODO: Find a way to get the cell_name and storage_names statically
|
||||
# (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
|
||||
# storage_names = cell_inst.mod.get_storage_net_names()
|
||||
# debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
|
||||
# "supported for characterization. Storage nets={0}").format(storage_names))
|
||||
cell_name = "xsram:xbank0:xbitcell_array:xbitcell_array:xbit_r0_c0"
|
||||
storage_names = ("Q", "Q_bar")
|
||||
q_name = cell_name + OPTS.hier_seperator + str(storage_names[0])
|
||||
qbar_name = cell_name + OPTS.hier_seperator + str(storage_names[1])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -82,6 +82,7 @@ class lib:
|
|||
debug.info(1, "Slews: {0}".format(self.slews))
|
||||
debug.info(1, "Loads: {0}".format(self.loads))
|
||||
debug.info(1, "self.load_slews : {0}".format(self.load_slews))
|
||||
|
||||
def create_corners(self):
|
||||
""" Create corners for characterization. """
|
||||
# Get the corners from the options file
|
||||
|
|
@ -253,7 +254,7 @@ class lib:
|
|||
self.lib.write(" resistance_unit : \"1kohm\" ;\n")
|
||||
self.lib.write(" capacitive_load_unit(1, pF) ;\n")
|
||||
self.lib.write(" leakage_power_unit : \"1mW\" ;\n")
|
||||
self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n")
|
||||
self.lib.write(" pulling_resistance_unit : \"1kohm\" ;\n")
|
||||
self.lib.write(" operating_conditions(OC){\n")
|
||||
self.lib.write(" process : {} ;\n".format(1.0)) # How to use TT, FF, SS?
|
||||
self.lib.write(" voltage : {} ;\n".format(self.voltage))
|
||||
|
|
@ -535,7 +536,7 @@ class lib:
|
|||
min_pulse_width = round_time(self.char_sram_results["min_period"])/2.0
|
||||
min_period = round_time(self.char_sram_results["min_period"])
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_type :\"min_pulse_width\"; \n")
|
||||
self.lib.write(" timing_type : \"min_pulse_width\"; \n")
|
||||
self.lib.write(" related_pin : clk{0}; \n".format(port))
|
||||
self.lib.write(" rise_constraint(scalar) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width))
|
||||
|
|
@ -545,7 +546,7 @@ class lib:
|
|||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_type :\"minimum_period\"; \n")
|
||||
self.lib.write(" timing_type : \"minimum_period\"; \n")
|
||||
self.lib.write(" related_pin : clk{0}; \n".format(port))
|
||||
self.lib.write(" rise_constraint(scalar) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(min_period))
|
||||
|
|
@ -801,7 +802,8 @@ class lib:
|
|||
|
||||
# information of checks
|
||||
# run it only the first time
|
||||
datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors))
|
||||
if OPTS.top_process != "memchar":
|
||||
datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors))
|
||||
|
||||
# write area
|
||||
datasheet.write(str(self.sram.width * self.sram.height) + ',')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -15,15 +15,16 @@ from .charutils import *
|
|||
class spice_measurement(ABC):
|
||||
"""Base class for spice stimulus measurements."""
|
||||
def __init__(self, measure_name, measure_scale=None, has_port=True):
|
||||
#Names must be unique for correct spice simulation, but not enforced here.
|
||||
# Names must be unique for correct spice simulation, but not enforced here.
|
||||
self.name = measure_name
|
||||
self.measure_scale = measure_scale
|
||||
self.has_port = has_port #Needed for error checking
|
||||
#Some meta values used externally. variables are added here for consistency accross the objects
|
||||
self.has_port = has_port # Needed for error checking
|
||||
# Some meta values used externally. variables are added here for consistency accross the objects
|
||||
self.meta_str = None
|
||||
self.meta_add_delay = False
|
||||
|
||||
@abstractmethod
|
||||
def get_measure_function(self):
|
||||
def measure_function(self):
|
||||
return None
|
||||
|
||||
@abstractmethod
|
||||
|
|
@ -31,28 +32,25 @@ class spice_measurement(ABC):
|
|||
return None
|
||||
|
||||
def write_measure(self, stim_obj, input_tuple):
|
||||
measure_func = self.get_measure_function()
|
||||
if measure_func == None:
|
||||
debug.error("Did not set measure function",1)
|
||||
measure_vals = self.get_measure_values(*input_tuple)
|
||||
measure_func(stim_obj, *measure_vals)
|
||||
self.measure_function(stim_obj, *measure_vals)
|
||||
|
||||
def retrieve_measure(self, port=None):
|
||||
self.port_error_check(port)
|
||||
if port != None:
|
||||
if port is not None:
|
||||
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
|
||||
else:
|
||||
value = parse_spice_list("timing", "{0}".format(self.name.lower()))
|
||||
if type(value)!=float or self.measure_scale == None:
|
||||
if type(value)!=float or self.measure_scale is None:
|
||||
return value
|
||||
else:
|
||||
return value*self.measure_scale
|
||||
return value * self.measure_scale
|
||||
|
||||
def port_error_check(self, port):
|
||||
if self.has_port and port == None:
|
||||
debug.error("Cannot retrieve measurement, port input was expected.",1)
|
||||
elif not self.has_port and port != None:
|
||||
debug.error("Unexpected port input received during measure retrieval.",1)
|
||||
if self.has_port and port is None:
|
||||
debug.error("Cannot retrieve measurement, port input was expected.", 1)
|
||||
elif not self.has_port and port is not None:
|
||||
debug.error("Unexpected port input received during measure retrieval.", 1)
|
||||
|
||||
|
||||
class delay_measure(spice_measurement):
|
||||
|
|
@ -71,8 +69,18 @@ class delay_measure(spice_measurement):
|
|||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||
self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd)
|
||||
|
||||
def get_measure_function(self):
|
||||
return stimuli.gen_meas_delay
|
||||
def measure_function(self, stim_obj, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
||||
""" Creates the .meas statement for the measurement of delay """
|
||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
||||
stim_obj.mf.write(measure_string.format(meas_name.lower(),
|
||||
trig_name,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
trig_td,
|
||||
targ_name,
|
||||
targ_val,
|
||||
targ_dir,
|
||||
targ_td))
|
||||
|
||||
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd):
|
||||
"""Set the constants for this measurement: signal names, directions, and trigger scales"""
|
||||
|
|
@ -91,7 +99,7 @@ class delay_measure(spice_measurement):
|
|||
trig_val = self.trig_val_of_vdd * vdd_voltage
|
||||
targ_val = self.targ_val_of_vdd * vdd_voltage
|
||||
|
||||
if port != None:
|
||||
if port is not None:
|
||||
# For dictionary indexing reasons, the name is formatted differently than the signals
|
||||
meas_name = "{}{}".format(self.name, port)
|
||||
trig_name = self.trig_name_no_port.format(port)
|
||||
|
|
@ -121,7 +129,7 @@ class slew_measure(delay_measure):
|
|||
self.trig_val_of_vdd = 0.9
|
||||
self.targ_val_of_vdd = 0.1
|
||||
else:
|
||||
debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1)
|
||||
debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str), 1)
|
||||
self.trig_name_no_port = signal_name
|
||||
self.targ_name_no_port = signal_name
|
||||
|
||||
|
|
@ -135,8 +143,18 @@ class power_measure(spice_measurement):
|
|||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||
self.set_meas_constants(power_type)
|
||||
|
||||
def get_measure_function(self):
|
||||
return stimuli.gen_meas_power
|
||||
def measure_function(self, stim_obj, meas_name, t_initial, t_final):
|
||||
""" Creates the .meas statement for the measurement of avg power """
|
||||
# power mea cmd is different in different spice:
|
||||
if OPTS.spice_name == "hspice":
|
||||
power_exp = "power"
|
||||
else:
|
||||
# FIXME: Obtain proper vdd and gnd name
|
||||
power_exp = "par('(-1*v(" + "vdd" + ")*I(v" + "vdd" + "))')"
|
||||
stim_obj.mf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(),
|
||||
power_exp,
|
||||
t_initial,
|
||||
t_final))
|
||||
|
||||
def set_meas_constants(self, power_type):
|
||||
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
||||
|
|
@ -146,7 +164,7 @@ class power_measure(spice_measurement):
|
|||
def get_measure_values(self, t_initial, t_final, port=None):
|
||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||
self.port_error_check(port)
|
||||
if port != None:
|
||||
if port is not None:
|
||||
meas_name = "{}{}".format(self.name, port)
|
||||
else:
|
||||
meas_name = self.name
|
||||
|
|
@ -160,8 +178,15 @@ class voltage_when_measure(spice_measurement):
|
|||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||
self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd)
|
||||
|
||||
def get_measure_function(self):
|
||||
return stimuli.gen_meas_find_voltage
|
||||
def measure_function(self, stim_obj, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
|
||||
""" Creates the .meas statement for the measurement of delay """
|
||||
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
|
||||
stim_obj.mf.write(measure_string.format(meas_name.lower(),
|
||||
targ_name,
|
||||
trig_name,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
trig_td))
|
||||
|
||||
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd):
|
||||
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
||||
|
|
@ -173,7 +198,7 @@ class voltage_when_measure(spice_measurement):
|
|||
def get_measure_values(self, trig_td, vdd_voltage, port=None):
|
||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||
self.port_error_check(port)
|
||||
if port != None:
|
||||
if port is not None:
|
||||
# For dictionary indexing reasons, the name is formatted differently than the signals
|
||||
meas_name = "{}{}".format(self.name, port)
|
||||
trig_name = self.trig_name_no_port.format(port)
|
||||
|
|
@ -194,8 +219,12 @@ class voltage_at_measure(spice_measurement):
|
|||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||
self.set_meas_constants(targ_name)
|
||||
|
||||
def get_measure_function(self):
|
||||
return stimuli.gen_meas_find_voltage_at_time
|
||||
def measure_function(self, stim_obj, meas_name, targ_name, time_at):
|
||||
""" Creates the .meas statement for voltage at time"""
|
||||
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
|
||||
stim_obj.mf.write(measure_string.format(meas_name.lower(),
|
||||
targ_name,
|
||||
time_at))
|
||||
|
||||
def set_meas_constants(self, targ_name):
|
||||
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
||||
|
|
@ -204,7 +233,7 @@ class voltage_at_measure(spice_measurement):
|
|||
def get_measure_values(self, time_at, port=None):
|
||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||
self.port_error_check(port)
|
||||
if port != None:
|
||||
if port is not None:
|
||||
# For dictionary indexing reasons, the name is formatted differently than the signals
|
||||
meas_name = "{}{}".format(self.name, port)
|
||||
targ_name = self.targ_name_no_port.format(port)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -42,7 +42,13 @@ class setup_hold():
|
|||
self.stim_sp = "sh_stim.sp"
|
||||
temp_stim = OPTS.openram_temp + self.stim_sp
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
|
||||
# creates and opens the measure file for writing
|
||||
self.meas_sp = "sh_meas.sp"
|
||||
temp_meas = OPTS.openram_temp + self.meas_sp
|
||||
self.mf = open(temp_meas, "w")
|
||||
|
||||
self.stim = stimuli(self.sf, self.mf, self.corner)
|
||||
|
||||
self.write_header(correct_value)
|
||||
|
||||
|
|
@ -56,6 +62,7 @@ class setup_hold():
|
|||
correct_value=correct_value)
|
||||
|
||||
self.write_clock()
|
||||
self.sf.write(".include {}\n".format(temp_meas))
|
||||
|
||||
self.write_measures(mode=mode,
|
||||
correct_value=correct_value)
|
||||
|
|
@ -63,6 +70,7 @@ class setup_hold():
|
|||
self.stim.write_control(4 * self.period)
|
||||
|
||||
self.sf.close()
|
||||
self.mf.close()
|
||||
|
||||
def write_header(self, correct_value):
|
||||
""" Write the header file with all the models and the power supplies. """
|
||||
|
|
@ -131,7 +139,7 @@ class setup_hold():
|
|||
else:
|
||||
dout_rise_or_fall = "FALL"
|
||||
|
||||
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
||||
self.mf.write("\n* Measure statements for pass/fail verification\n")
|
||||
trig_name = "clk"
|
||||
targ_name = "Q"
|
||||
trig_val = targ_val = 0.5 * self.vdd_voltage
|
||||
|
|
@ -168,29 +176,33 @@ class setup_hold():
|
|||
target_time=feasible_bound,
|
||||
correct_value=correct_value)
|
||||
self.stim.run_sim(self.stim_sp)
|
||||
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||
ideal_clk_to_q = convert_to_float(parse_spice_list("timing",
|
||||
"clk2q_delay"))
|
||||
# We use a 1/2 speed clock for some reason...
|
||||
setuphold_time = (feasible_bound - 2 * self.period)
|
||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||
passing_setuphold_time = -1 * setuphold_time
|
||||
else:
|
||||
passing_setuphold_time = setuphold_time
|
||||
debug.info(2, "*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode,
|
||||
correct_value,
|
||||
ideal_clk_to_q,
|
||||
setuphold_time))
|
||||
debug.info(2, "*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}"
|
||||
.format(mode,
|
||||
correct_value,
|
||||
ideal_clk_to_q,
|
||||
setuphold_time))
|
||||
|
||||
if type(ideal_clk_to_q)!=float:
|
||||
debug.error("Initial hold time fails for data value feasible "
|
||||
"bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,
|
||||
ideal_clk_to_q,
|
||||
setuphold_time),
|
||||
"bound {0} Clk-to-Q {1} Setup/Hold {2}"
|
||||
.format(feasible_bound,
|
||||
ideal_clk_to_q,
|
||||
setuphold_time),
|
||||
2)
|
||||
|
||||
debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
|
||||
setuphold_time,
|
||||
feasible_bound,
|
||||
2 * self.period))
|
||||
debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} "
|
||||
.format(mode,
|
||||
setuphold_time,
|
||||
feasible_bound,
|
||||
2 * self.period))
|
||||
|
||||
while True:
|
||||
target_time = (feasible_bound + infeasible_bound) / 2
|
||||
|
|
@ -198,11 +210,12 @@ class setup_hold():
|
|||
target_time=target_time,
|
||||
correct_value=correct_value)
|
||||
|
||||
debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
|
||||
correct_value,
|
||||
target_time,
|
||||
infeasible_bound,
|
||||
feasible_bound))
|
||||
debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}"
|
||||
.format(mode,
|
||||
correct_value,
|
||||
target_time,
|
||||
infeasible_bound,
|
||||
feasible_bound))
|
||||
|
||||
self.stim.run_sim(self.stim_sp)
|
||||
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -235,7 +235,7 @@ class simulation():
|
|||
self.add_wmask(wmask, port)
|
||||
self.add_spare_wen("1" * self.num_spare_cols, port)
|
||||
|
||||
#Add noops to all other ports.
|
||||
# Add noops to all other ports.
|
||||
for unselected_port in self.all_ports:
|
||||
if unselected_port != port:
|
||||
self.add_noop_one_port(unselected_port)
|
||||
|
|
@ -267,7 +267,7 @@ class simulation():
|
|||
self.add_wmask("0" * self.num_wmasks, port)
|
||||
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||
|
||||
#Add noops to all other ports.
|
||||
# Add noops to all other ports.
|
||||
for unselected_port in self.all_ports:
|
||||
if unselected_port != port:
|
||||
self.add_noop_one_port(unselected_port)
|
||||
|
|
@ -356,14 +356,14 @@ class simulation():
|
|||
|
||||
self.add_noop_one_port(port)
|
||||
|
||||
#Add noops to all other ports.
|
||||
# Add noops to all other ports.
|
||||
for unselected_port in self.all_ports:
|
||||
if unselected_port != port:
|
||||
self.add_noop_one_port(unselected_port)
|
||||
|
||||
def append_cycle_comment(self, port, comment):
|
||||
"""Add comment to list to be printed in stimulus file"""
|
||||
#Clean up time before appending. Make spacing dynamic as well.
|
||||
# Clean up time before appending. Make spacing dynamic as well.
|
||||
time = "{0:.2f} ns:".format(self.t_current)
|
||||
time_spacing = len(time) + 6
|
||||
self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
|
||||
|
|
@ -388,7 +388,7 @@ class simulation():
|
|||
split_word2 = [x + '_' * (n != 0 and n % 4 == 0) for n, x in enumerate(split_word)]
|
||||
# Join the word unreversed back together
|
||||
new_word = ''.join(reversed(split_word2))
|
||||
return(new_word)
|
||||
return (new_word)
|
||||
|
||||
# Split extra cols
|
||||
if self.num_spare_cols > 0:
|
||||
|
|
@ -414,9 +414,9 @@ class simulation():
|
|||
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
|
||||
addr,
|
||||
port,
|
||||
int(t_current/self.period),
|
||||
int(t_current / self.period),
|
||||
t_current,
|
||||
t_current+self.period)
|
||||
t_current + self.period)
|
||||
elif op == "partial_write":
|
||||
str = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)"
|
||||
comment = str.format(word,
|
||||
|
|
@ -454,7 +454,7 @@ class simulation():
|
|||
for i in range(abits):
|
||||
pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
|
||||
|
||||
#Control signals not finalized.
|
||||
# Control signals not finalized.
|
||||
for port in range(total_ports):
|
||||
pin_names.append("CSB{0}".format(port))
|
||||
for port in range(total_ports):
|
||||
|
|
@ -493,11 +493,12 @@ class simulation():
|
|||
|
||||
# other initializations can only be done during analysis when a bit has been selected
|
||||
# for testing.
|
||||
self.sram.bank.graph_exclude_precharge()
|
||||
self.sram.graph_exclude_addr_dff()
|
||||
self.sram.graph_exclude_data_dff()
|
||||
self.sram.graph_exclude_ctrl_dffs()
|
||||
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
|
||||
if OPTS.top_process != 'memchar':
|
||||
self.sram.bank.graph_exclude_precharge()
|
||||
self.sram.graph_exclude_addr_dff()
|
||||
self.sram.graph_exclude_data_dff()
|
||||
self.sram.graph_exclude_ctrl_dffs()
|
||||
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
|
||||
|
||||
def set_internal_spice_names(self):
|
||||
"""
|
||||
|
|
@ -515,9 +516,9 @@ class simulation():
|
|||
self.sen_name = sen_with_port
|
||||
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
|
||||
|
||||
column_addr = self.get_column_addr()
|
||||
# column_addr = self.get_column_addr()
|
||||
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
|
||||
port_pos = -1 - len(str(column_addr)) - len(str(port))
|
||||
# port_pos = -1 - len(str(column_addr)) - len(str(port))
|
||||
|
||||
if bl_name_port.endswith(str(port) + "_" + str(self.bitline_column)): # single port SRAM case, bl will not be numbered eg bl_0
|
||||
self.bl_name = bl_name_port
|
||||
|
|
@ -535,7 +536,7 @@ class simulation():
|
|||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||
|
||||
self.sen_name = self.get_sen_name(self.graph.all_paths)
|
||||
#debug.info(2, "s_en {}".format(self.sen_name))
|
||||
# debug.info(2, "s_en {}".format(self.sen_name))
|
||||
|
||||
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
|
||||
self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1)
|
||||
|
|
@ -563,10 +564,10 @@ class simulation():
|
|||
Creates timing graph to generate the timing paths for the SRAM output.
|
||||
"""
|
||||
|
||||
#Make exclusions dependent on the bit being tested.
|
||||
# Make exclusions dependent on the bit being tested.
|
||||
self.sram.clear_exclude_bits() # Removes previous bit exclusions
|
||||
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
|
||||
port=self.read_ports[0] #FIXME, port_data requires a port specification, assuming single port for now
|
||||
port=self.read_ports[0] # FIXME, port_data requires a port specification, assuming single port for now
|
||||
if self.words_per_row > 1:
|
||||
self.sram.graph_clear_column_mux(port)
|
||||
self.sram.graph_exclude_column_mux(self.bitline_column, port)
|
||||
|
|
@ -594,7 +595,7 @@ class simulation():
|
|||
for path in paths:
|
||||
aliases = self.sram.find_aliases(self.sram_instance_name, self.pins, path, internal_net, mod, exclusion_set)
|
||||
if net_found and len(aliases) >= 1:
|
||||
debug.error('Found multiple paths with {} net.'.format(internal_net), 1)
|
||||
pass #debug.error('Found multiple paths with {} net.'.format(internal_net), 1)
|
||||
elif len(aliases) > 1:
|
||||
debug.error('Found multiple {} nets in single path.'.format(internal_net), 1)
|
||||
elif not net_found and len(aliases) == 1:
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class sram_op(Enum):
|
||||
READ_ZERO = 0
|
||||
READ_ONE = 1
|
||||
WRITE_ZERO = 2
|
||||
WRITE_ONE = 3
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -22,7 +22,7 @@ from openram import OPTS
|
|||
class stimuli():
|
||||
""" Class for providing stimuli functions """
|
||||
|
||||
def __init__(self, stim_file, corner):
|
||||
def __init__(self, stim_file, meas_file, corner):
|
||||
self.vdd_name = "vdd"
|
||||
self.gnd_name = "gnd"
|
||||
self.pmos_name = tech.spice["pmos"]
|
||||
|
|
@ -31,6 +31,7 @@ class stimuli():
|
|||
self.tx_length = tech.drc["minlength_channel"]
|
||||
|
||||
self.sf = stim_file
|
||||
self.mf = meas_file
|
||||
|
||||
(self.process, self.voltage, self.temperature) = corner
|
||||
found = False
|
||||
|
|
@ -136,7 +137,7 @@ class stimuli():
|
|||
offset,
|
||||
t_rise,
|
||||
t_fall,
|
||||
0.5*period-0.5*t_rise-0.5*t_fall,
|
||||
0.5 * period - 0.5 * t_rise - 0.5 * t_fall,
|
||||
period))
|
||||
|
||||
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
|
||||
|
|
@ -181,7 +182,7 @@ class stimuli():
|
|||
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
||||
""" Creates the .meas statement for the measurement of delay """
|
||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
||||
self.sf.write(measure_string.format(meas_name.lower(),
|
||||
self.mf.write(measure_string.format(meas_name.lower(),
|
||||
trig_name,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
|
|
@ -194,7 +195,7 @@ class stimuli():
|
|||
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
|
||||
""" Creates the .meas statement for the measurement of delay """
|
||||
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
|
||||
self.sf.write(measure_string.format(meas_name.lower(),
|
||||
self.mf.write(measure_string.format(meas_name.lower(),
|
||||
targ_name,
|
||||
trig_name,
|
||||
trig_val,
|
||||
|
|
@ -204,7 +205,7 @@ class stimuli():
|
|||
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
|
||||
""" Creates the .meas statement for voltage at time"""
|
||||
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
|
||||
self.sf.write(measure_string.format(meas_name.lower(),
|
||||
self.mf.write(measure_string.format(meas_name.lower(),
|
||||
targ_name,
|
||||
time_at))
|
||||
|
||||
|
|
@ -215,15 +216,15 @@ class stimuli():
|
|||
power_exp = "power"
|
||||
else:
|
||||
power_exp = "par('(-1*v(" + str(self.vdd_name) + ")*I(v" + str(self.vdd_name) + "))')"
|
||||
self.sf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(),
|
||||
self.mf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(),
|
||||
power_exp,
|
||||
t_initial,
|
||||
t_final))
|
||||
|
||||
def gen_meas_value(self, meas_name, dout, t_initial, t_final):
|
||||
measure_string=".meas tran {0} FIND v({1}) AT={2}n\n\n".format(meas_name.lower(), dout, (t_initial + t_final) / 2)
|
||||
#measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final)
|
||||
self.sf.write(measure_string)
|
||||
# measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final)
|
||||
self.mf.write(measure_string)
|
||||
|
||||
def write_control(self, end_time, runlvl=4):
|
||||
""" Write the control cards to run and end the simulation """
|
||||
|
|
@ -278,7 +279,7 @@ class stimuli():
|
|||
self.sf.write(".OPTIONS DEVICE TEMP={}\n".format(self.temperature))
|
||||
self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n")
|
||||
self.sf.write(".OPTIONS LINSOL type=klu\n")
|
||||
self.sf.write(".OPTIONS TIMEINT RELTOL=1e-6 ABSTOL=1e-10 method=gear minorder=2\n")
|
||||
self.sf.write(".OPTIONS TIMEINT RELTOL=1e-3 ABSTOL=1e-6 method=gear minorder=2\n")
|
||||
# Format: .TRAN <initial step> <final time> <start time> <step ceiling>
|
||||
self.sf.write(".TRAN {0}p {1}n 0n {0}p\n".format(timestep, end_time))
|
||||
elif OPTS.spice_name:
|
||||
|
|
@ -409,14 +410,14 @@ class stimuli():
|
|||
# FIXME: Should use verify/run_script.py here but run_script doesn't return
|
||||
# the return code of the subprocess. File names might also mismatch.
|
||||
from openram import CONDA_HOME
|
||||
cmd = "source {0}/bin/activate && {1} && conda deactivate".format(CONDA_HOME, cmd)
|
||||
cmd = "/bin/bash -c 'source {0}/bin/activate && {1} && conda deactivate'".format(CONDA_HOME, cmd)
|
||||
debug.info(2, cmd)
|
||||
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
||||
proc = subprocess.run(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
||||
|
||||
spice_stdout.close()
|
||||
spice_stderr.close()
|
||||
|
||||
if (retcode > valid_retcode):
|
||||
if (proc.returncode > valid_retcode):
|
||||
debug.error("Spice simulation error: " + cmd, -1)
|
||||
else:
|
||||
end_time = datetime.datetime.now()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -85,14 +85,15 @@ class trim_spice():
|
|||
|
||||
wl_regex = r"wl\d*_{}".format(wl_address)
|
||||
bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address))
|
||||
bl_no_port_regex = r"bl_{}".format(int(self.words_per_row*data_bit + col_address))
|
||||
|
||||
self.remove_insts("bitcell_array",[wl_regex,bl_regex])
|
||||
|
||||
# 2. Keep sense amps basd on BL
|
||||
# FIXME: The bit lines are not indexed the same in sense_amp_array
|
||||
#self.remove_insts("sense_amp_array",[bl_regex])
|
||||
self.remove_insts("sense_amp_array",[bl_no_port_regex])
|
||||
|
||||
# 3. Keep column muxes basd on BL
|
||||
self.remove_insts("column_mux_array", [bl_regex])
|
||||
self.remove_insts("single_level_column_mux_array", [bl_no_port_regex])
|
||||
|
||||
# 4. Keep write driver based on DATA
|
||||
data_regex = r"data_{}".format(data_bit)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .datasheet_gen import datasheet_gen
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
import pdb
|
||||
import inspect
|
||||
from openram import globals
|
||||
|
|
@ -62,6 +63,9 @@ def print_raw(str):
|
|||
|
||||
|
||||
def log(str):
|
||||
# Add timestamp at the beginning of the string
|
||||
timestr = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
str = "[{}] {}".format(timestr, str)
|
||||
if globals.OPTS.output_name != '':
|
||||
if log.create_file:
|
||||
# We may have not yet read the config, so we need to ensure
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .custom_cell_properties import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -15,6 +15,7 @@ corner, but should probably be extended.
|
|||
|
||||
import sys
|
||||
from globals import *
|
||||
from importlib import reload
|
||||
|
||||
(OPTS, args) = parse_args()
|
||||
|
||||
|
|
@ -41,36 +42,29 @@ slew = float(args[3])
|
|||
import debug
|
||||
|
||||
init_openram(config_file=config_file, is_unit_test=False)
|
||||
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
write_size=OPTS.write_size,
|
||||
num_banks=OPTS.num_banks,
|
||||
words_per_row=OPTS.words_per_row,
|
||||
num_spare_rows=OPTS.num_spare_rows,
|
||||
num_spare_cols=OPTS.num_spare_cols)
|
||||
|
||||
OPTS.netlist_only = True
|
||||
OPTS.check_lvsdrc = False
|
||||
# Put the temp output in the output path since it is what we want to generate!
|
||||
old_openram_temp = OPTS.openram_temp
|
||||
OPTS.openram_temp = OPTS.output_path
|
||||
|
||||
|
||||
|
||||
import sram
|
||||
class fake_sram(sram.sram):
|
||||
""" This is an SRAM that doesn't actually create itself, just computes
|
||||
the sizes. """
|
||||
def __init__(self, word_size, num_words, num_banks, name, num_spare_rows):
|
||||
self.name = name
|
||||
self.word_size = word_size
|
||||
self.num_words = num_words
|
||||
self.num_banks = num_banks
|
||||
self.num_spare_rows = num_spare_rows
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
# to get the row, col, etc.
|
||||
self.compute_sizes()
|
||||
|
||||
sram = fake_sram(OPTS.word_size, OPTS.num_words, OPTS.num_banks, OPTS.output_name)
|
||||
sp_file = OPTS.output_path+OPTS.output_name + ".sp"
|
||||
from sram import sram
|
||||
s = sram(name=OPTS.output_name, sram_config=c)
|
||||
|
||||
from characterizer import delay
|
||||
import tech
|
||||
# Set up the delay and set to the nominal corner
|
||||
d = delay.delay(sram, sp_file, ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"]))
|
||||
d = delay(s, s.get_sp_name(), ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"]))
|
||||
# Set the period
|
||||
d.period = period
|
||||
# Set the load of outputs and slew of inputs
|
||||
|
|
@ -91,4 +85,3 @@ print("Output files are:\n{0}stim.sp\n{0}sram.sp\n{0}reduced.sp".format(OPTS.out
|
|||
OPTS.openram_temp = old_openram_temp
|
||||
# Delete temp files, remove the dir, etc.
|
||||
end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
|
|
@ -146,7 +146,7 @@ def check_versions():
|
|||
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
||||
debug.error("Python {0}.{1} or greater is required.".format(major_required, minor_required), -1)
|
||||
|
||||
# Verify any version of git is isntalled before proceeding
|
||||
# Verify any version of git is installed before proceeding
|
||||
try:
|
||||
subprocess.check_output(["git", "--version"])
|
||||
except:
|
||||
|
|
@ -482,7 +482,6 @@ def init_paths():
|
|||
#pprint(s)
|
||||
#print("Test {0} in dir {1}".format(s[2].filename, OPTS.openram_temp))
|
||||
|
||||
|
||||
# Don't delete the output dir, it may have other files!
|
||||
# make the directory if it doesn't exist
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
model_name = "cacti"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from .shared_config import *
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue