Make initial public release

This commit is contained in:
Joachim Strömbergson 2022-09-19 08:51:11 +02:00 committed by Daniel Lublin
commit 715de60f4a
251 changed files with 881225 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Helping linguist detect files as Verilog, not Coq.
*.v linguist-language=Verilog

66
.gitignore vendored Normal file
View File

@ -0,0 +1,66 @@
/tests/*.o
/firmware/*.o
/firmware/firmware.bin
/firmware/firmware.elf
/firmware/firmware.hex
/firmware/firmware.map
/dhrystone/dhry.bin
/dhrystone/dhry.elf
/dhrystone/dhry.hex
/dhrystone/dhry.map
/dhrystone/testbench.vvp
/dhrystone/testbench.vcd
/dhrystone/testbench_nola.vvp
/dhrystone/testbench_nola.vcd
/dhrystone/timing.vvp
/dhrystone/timing.txt
/dhrystone/*.d
/dhrystone/*.o
/riscv-gnu-toolchain-riscv32i
/riscv-gnu-toolchain-riscv32ic
/riscv-gnu-toolchain-riscv32im
/riscv-gnu-toolchain-riscv32imc
/testbench.vvp
/testbench_wb.vvp
/testbench_ez.vvp
/testbench_sp.vvp
/testbench_rvf.vvp
/testbench_synth.vvp
/testbench.gtkw
/testbench.vcd
/testbench.trace
/testbench_verilator*
/check.smt2
/check.vcd
synth.log
synth.v
*.o
*.asc
*.bin
*.elf
*.map
synth.*
*.tmp
*.hex
!uds.hex
!udi.hex
.*.swp
*.sch-bak
*.sim
# Ignore list for KiCAD Projects
*.000
*.bak
*.bck
*.kicad_pcb-bak
*.sch-bak
_autosave-*
*-backups
\#auto_saved_files#
*.tmp
*-save.pro
*-save.kicad_pcb
fp-info-cache
*.net
*.dsn
*.ses

23
LICENSES/README.md Normal file
View File

@ -0,0 +1,23 @@
# Tillitis Key 1 Licensing
## Main license
Unless otherwise noted, the project sources are licensed under the
terms and conditions of the "GNU General Public License v2.0 only" and
hardware boards under "CERN Open Hardware Licence Version 2 - Strongly
Reciprocal".
The `LICENSES/` directory contains copies of the license texts used by
the sources included in the project source tree.
## SPDX
The project uses single-line references to Unique License Identifiers
as defined by the Linux Foundation's [SPDX project](https://spdx.org/).
The line in each individual source file identifies the license
applicable to that file.
The current set of valid, predefined SPDX identifiers can be found on
the SPDX License List at:
https://spdx.org/licenses/

23
LICENSES/bsd-2.txt Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) <year>, <owner>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

116
LICENSES/cc-universal.txt Normal file
View File

@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

289
LICENSES/cern-ohl-v2.txt Normal file
View File

@ -0,0 +1,289 @@
CERN Open Hardware Licence Version 2 - Strongly Reciprocal
Preamble
CERN has developed this licence to promote collaboration among
hardware designers and to provide a legal tool which supports the
freedom to use, study, modify, share and distribute hardware designs
and products based on those designs. Version 2 of the CERN Open
Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this
licence, CERN-OHL-S (strongly reciprocal).
The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
unmodified form only.
Use of this Licence does not imply any endorsement by CERN of any
Licensor or their designs nor does it imply any involvement by CERN in
their development.
1 Definitions
1.1 'Licence' means this CERN-OHL-S.
1.2 'Compatible Licence' means
a) any earlier version of the CERN Open Hardware licence, or
b) any version of the CERN-OHL-S, or
c) any licence which permits You to treat the Source to which
it applies as licensed under CERN-OHL-S provided that on
Conveyance of any such Source, or any associated Product You
treat the Source in question as being licensed under
CERN-OHL-S.
1.3 'Source' means information such as design materials or digital
code which can be applied to Make or test a Product or to
prepare a Product for use, Conveyance or sale, regardless of its
medium or how it is expressed. It may include Notices.
1.4 'Covered Source' means Source that is explicitly made available
under this Licence.
1.5 'Product' means any device, component, work or physical object,
whether in finished or intermediate form, arising from the use,
application or processing of Covered Source.
1.6 'Make' means to create or configure something, whether by
manufacture, assembly, compiling, loading or applying Covered
Source or another Product or otherwise.
1.7 'Available Component' means any part, sub-assembly, library or
code which:
a) is licensed to You as Complete Source under a Compatible
Licence; or
b) is available, at the time a Product or the Source containing
it is first Conveyed, to You and any other prospective
licensees
i) as a physical part with sufficient rights and
information (including any configuration and
programming files and information about its
characteristics and interfaces) to enable it either to
be Made itself, or to be sourced and used to Make the
Product; or
ii) as part of the normal distribution of a tool used to
design or Make the Product.
1.8 'Complete Source' means the set of all Source necessary to Make
a Product, in the preferred form for making modifications,
including necessary installation and interfacing information
both for the Product, and for any included Available Components.
If the format is proprietary, it must also be made available in
a format (if the proprietary tool can create it) which is
viewable with a tool available to potential licensees and
licensed under a licence approved by the Free Software
Foundation or the Open Source Initiative. Complete Source need
not include the Source of any Available Component, provided that
You include in the Complete Source sufficient information to
enable a recipient to Make or source and use the Available
Component to Make the Product.
1.9 'Source Location' means a location where a Licensor has placed
Covered Source, and which that Licensor reasonably believes will
remain easily accessible for at least three years for anyone to
obtain a digital copy.
1.10 'Notice' means copyright, acknowledgement and trademark notices,
Source Location references, modification notices (subsection
3.3(b)) and all notices that refer to this Licence and to the
disclaimer of warranties that are included in the Covered
Source.
1.11 'Licensee' or 'You' means any person exercising rights under
this Licence.
1.12 'Licensor' means a natural or legal person who creates or
modifies Covered Source. A person may be a Licensee and a
Licensor at the same time.
1.13 'Convey' means to communicate to the public or distribute.
2 Applicability
2.1 This Licence governs the use, copying, modification, Conveying
of Covered Source and Products, and the Making of Products. By
exercising any right granted under this Licence, You irrevocably
accept these terms and conditions.
2.2 This Licence is granted by the Licensor directly to You, and
shall apply worldwide and without limitation in time.
2.3 You shall not attempt to restrict by contract or otherwise the
rights granted under this Licence to other Licensees.
2.4 This Licence is not intended to restrict fair use, fair dealing,
or any other similar right.
3 Copying, Modifying and Conveying Covered Source
3.1 You may copy and Convey verbatim copies of Covered Source, in
any medium, provided You retain all Notices.
3.2 You may modify Covered Source, other than Notices, provided that
You irrevocably undertake to make that modified Covered Source
available from a Source Location should You Convey a Product in
circumstances where the recipient does not otherwise receive a
copy of the modified Covered Source. In each case subsection 3.3
shall apply.
You may only delete Notices if they are no longer applicable to
the corresponding Covered Source as modified by You and You may
add additional Notices applicable to Your modifications.
Including Covered Source in a larger work is modifying the
Covered Source, and the larger work becomes modified Covered
Source.
3.3 You may Convey modified Covered Source (with the effect that You
shall also become a Licensor) provided that You:
a) retain Notices as required in subsection 3.2;
b) add a Notice to the modified Covered Source stating that You
have modified it, with the date and brief description of how
You have modified it;
c) add a Source Location Notice for the modified Covered Source
if You Convey in circumstances where the recipient does not
otherwise receive a copy of the modified Covered Source; and
d) license the modified Covered Source under the terms and
conditions of this Licence (or, as set out in subsection
8.3, a later version, if permitted by the licence of the
original Covered Source). Such modified Covered Source must
be licensed as a whole, but excluding Available Components
contained in it, which remain licensed under their own
applicable licences.
4 Making and Conveying Products
You may Make Products, and/or Convey them, provided that You either
provide each recipient with a copy of the Complete Source or ensure
that each recipient is notified of the Source Location of the Complete
Source. That Complete Source is Covered Source, and You must
accordingly satisfy Your obligations set out in subsection 3.3. If
specified in a Notice, the Product must visibly and securely display
the Source Location on it or its packaging or documentation in the
manner specified in that Notice.
5 Research and Development
You may Convey Covered Source, modified Covered Source or Products to
a legal entity carrying out development, testing or quality assurance
work on Your behalf provided that the work is performed on terms which
prevent the entity from both using the Source or Products for its own
internal purposes and Conveying the Source or Products or any
modifications to them to any person other than You. Any modifications
made by the entity shall be deemed to be made by You pursuant to
subsection 3.2.
6 DISCLAIMER AND LIABILITY
6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
are provided 'as is' and any express or implied warranties,
including, but not limited to, implied warranties of
merchantability, of satisfactory quality, non-infringement of
third party rights, and fitness for a particular purpose or use
are disclaimed in respect of any Source or Product to the
maximum extent permitted by law. The Licensor makes no
representation that any Source or Product does not or will not
infringe any patent, copyright, trade secret or other
proprietary right. The entire risk as to the use, quality, and
performance of any Source or Product shall be with You and not
the Licensor. This disclaimer of warranty is an essential part
of this Licence and a condition for the grant of any rights
granted under this Licence.
6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
the maximum extent permitted by law, have no liability for
direct, indirect, special, incidental, consequential, exemplary,
punitive or other damages of any character including, without
limitation, procurement of substitute goods or services, loss of
use, data or profits, or business interruption, however caused
and on any theory of contract, warranty, tort (including
negligence), product liability or otherwise, arising in any way
in relation to the Covered Source, modified Covered Source
and/or the Making or Conveyance of a Product, even if advised of
the possibility of such damages, and You shall hold the
Licensor(s) free and harmless from any liability, costs,
damages, fees and expenses, including claims by third parties,
in relation to such use.
7 Patents
7.1 Subject to the terms and conditions of this Licence, each
Licensor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as
stated in subsections 7.2 and 8.4) patent licence to Make, have
Made, use, offer to sell, sell, import, and otherwise transfer
the Covered Source and Products, where such licence applies only
to those patent claims licensable by such Licensor that are
necessarily infringed by exercising rights under the Covered
Source as Conveyed by that Licensor.
7.2 If You institute patent litigation against any entity (including
a cross-claim or counterclaim in a lawsuit) alleging that the
Covered Source or a Product constitutes direct or contributory
patent infringement, or You seek any declaration that a patent
licensed to You under this Licence is invalid or unenforceable
then any rights granted to You under this Licence shall
terminate as of the date such process is initiated.
8 General
8.1 If any provisions of this Licence are or subsequently become
invalid or unenforceable for any reason, the remaining
provisions shall remain effective.
8.2 You shall not use any of the name (including acronyms and
abbreviations), image, or logo by which the Licensor or CERN is
known, except where needed to comply with section 3, or where
the use is otherwise allowed by law. Any such permitted use
shall be factual and shall not be made so as to suggest any kind
of endorsement or implication of involvement by the Licensor or
its personnel.
8.3 CERN may publish updated versions and variants of this Licence
which it considers to be in the spirit of this version, but may
differ in detail to address new problems or concerns. New
versions will be published with a unique version number and a
variant identifier specifying the variant. If the Licensor has
specified that a given variant applies to the Covered Source
without specifying a version, You may treat that Covered Source
as being released under any version of the CERN-OHL with that
variant. If no variant is specified, the Covered Source shall be
treated as being released under CERN-OHL-S. The Licensor may
also specify that the Covered Source is subject to a specific
version of the CERN-OHL or any later version in which case You
may apply this or any later version of CERN-OHL with the same
variant identifier published by CERN.
8.4 This Licence shall terminate with immediate effect if You fail
to comply with any of its terms and conditions.
8.5 However, if You cease all breaches of this Licence, then Your
Licence from any Licensor is reinstated unless such Licensor has
terminated this Licence by giving You, while You remain in
breach, a notice specifying the breach and requiring You to cure
it within 30 days, and You have failed to come into compliance
in all material respects by the end of the 30 day period. Should
You repeat the breach after receipt of a cure notice and
subsequent reinstatement, this Licence will terminate
immediately and permanently. Section 6 shall continue to apply
after any termination.
8.6 This Licence shall not be enforceable except by a Licensor
acting as such, and third party beneficiary rights are
specifically excluded.

245
LICENSES/gpl-v2.only.txt Normal file
View File

@ -0,0 +1,245 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and
change it. By contrast, the GNU General Public License is intended to guarantee your
freedom to share and change free software--to make sure the software is free for all
its users. This General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to using it.
(Some other Free Software Foundation software is covered by the GNU Lesser General
Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General
Public Licenses are designed to make sure that you have the freedom to distribute
copies of free software (and charge for this service if you wish), that you receive
source code or can get it if you want it, that you can change the software or use
pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you
these rights or to ask you to surrender the rights. These restrictions translate to
certain responsibilities for you if you distribute copies of the software, or if you
modify it.
For example, if you distribute copies of such a program, whether gratis or for a
fee, you must give the recipients all the rights that you have. You must make sure
that they, too, receive or can get the source code. And you must show them these
terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you
this license which gives you legal permission to copy, distribute and/or modify the
software.
Also, for each author's protection and ours, we want to make certain that everyone
understands that there is no warranty for this free software. If the software is
modified by someone else and passed on, we want its recipients to know that what
they have is not the original, so that any problems introduced by others will not
reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to
avoid the danger that redistributors of a free program will individually obtain
patent licenses, in effect making the program proprietary. To prevent this, we have
made it clear that any patent must be licensed for everyone's free use or not
licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed
by the copyright holder saying it may be distributed under the terms of this General
Public License. The "Program", below, refers to any such program or work, and a
"work based on the Program" means either the Program or any derivative work under
copyright law: that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another language.
(Hereinafter, translation is included without limitation in the term
"modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by this
License; they are outside its scope. The act of running the Program is not
restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made by running
the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and appropriately publish
on each copy an appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any warranty; and
give any other recipients of the Program a copy of this License along with the
Program.
You may charge a fee for the physical act of transferring a copy, and you may at
your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus
forming a work based on the Program, and copy and distribute such modifications or
work under the terms of Section 1 above, provided that you also meet all of these
conditions:
a) You must cause the modified files to carry prominent notices stating that you
changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in
part contains or is derived from the Program or any part thereof, to be licensed
as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you
must cause it, when started running for such interactive use in the most
ordinary way, to print or display an announcement including an appropriate
copyright notice and a notice that there is no warranty (or else, saying that
you provide a warranty) and that users may redistribute the program under these
conditions, and telling the user how to view a copy of this License. (Exception:
if the Program itself is interactive but does not normally print such an
announcement, your work based on the Program is not required to print an
announcement.)
These requirements apply to the modified work as a whole. If identifiable sections
of that work are not derived from the Program, and can be reasonably considered
independent and separate works in themselves, then this License, and its terms, do
not apply to those sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based on the
Program, the distribution of the whole must be on the terms of this License, whose
permissions for other licensees extend to the entire whole, and thus to each and
every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to
work written entirely by you; rather, the intent is to exercise the right to control
the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the
Program (or with a work based on the Program) on a volume of a storage or
distribution medium does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2)
in object code or executable form under the terms of Sections 1 and 2 above provided
that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source code,
which must be distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give
any third party, for a charge no more than your cost of physically performing
source distribution, a complete machine-readable copy of the corresponding
source code, to be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute
corresponding source code. (This alternative is allowed only for noncommercial
distribution and only if you received the program in object code or executable
form with such an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means all the
source code for all modules it contains, plus any associated interface definition
files, plus the scripts used to control compilation and installation of the
executable. However, as a special exception, the source code distributed need not
include anything that is normally distributed (in either source or binary form) with
the major components (compiler, kernel, and so on) of the operating system on which
the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from
a designated place, then offering equivalent access to copy the source code from the
same place counts as distribution of the source code, even though third parties are
not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense or distribute the Program is void, and will automatically terminate your
rights under this License. However, parties who have received copies, or rights,
from you under this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the Program or
its derivative works. These actions are prohibited by law if you do not accept this
License. Therefore, by modifying or distributing the Program (or any work based on
the Program), you indicate your acceptance of this License to do so, and all its
terms and conditions for copying, distributing or modifying the Program or works
based on it.
6. Each time you redistribute the Program (or any work based on the Program), the
recipient automatically receives a license from the original licensor to copy,
distribute or modify the Program subject to these terms and conditions. You may not
impose any further restrictions on the recipients' exercise of the rights granted
herein. You are not responsible for enforcing compliance by third parties to this
License.
7. If, as a consequence of a court judgment or allegation of patent infringement or
for any other reason (not limited to patent issues), conditions are imposed on you
(whether by court order, agreement or otherwise) that contradict the conditions of
this License, they do not excuse you from the conditions of this License. If you
cannot distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may not
distribute the Program at all. For example, if a patent license would not permit
royalty-free redistribution of the Program by all those who receive copies directly
or indirectly through you, then the only way you could satisfy both it and this
License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular
circumstance, the balance of the section is intended to apply and the section as a
whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other
property right claims or to contest validity of any such claims; this section has
the sole purpose of protecting the integrity of the free software distribution
system, which is implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed through that system
in reliance on consistent application of that system; it is up to the author/donor
to decide if he or she is willing to distribute software through any other system
and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain countries
either by patents or by copyrighted interfaces, the original copyright holder who
places the Program under this License may add an explicit geographical distribution
limitation excluding those countries, so that distribution is permitted only in or
among countries not thus excluded. In such case, this License incorporates the
limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of the
General Public License from time to time. Such new versions will be similar in
spirit to the present version, but may differ in detail to address new problems or
concerns.
Each version is given a distinguishing version number. If the Program specifies a
version number of this License which applies to it and "any later version", you have
the option of following the terms and conditions either of that version or of any
later version published by the Free Software Foundation. If the Program does not
specify a version number of this License, you may choose any version ever published
by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs whose
distribution conditions are different, write to the author to ask for permission.
For software which is copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our decision will be
guided by the two goals of preserving the free status of all derivatives of our free
software and of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE
PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN
WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

15
LICENSES/isc.txt Normal file
View File

@ -0,0 +1,15 @@
ISC License
Copyright (C) <year> <owner>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

50
README.md Normal file
View File

@ -0,0 +1,50 @@
# Tillitis Key 1
## Introduction
Tillitis Key 1 is a new kind of USB security token. All of its
software, FPGA logic, schematics, and PCB layout are open source, as
all security software and hardware should be. This in itself makes it
different, as other security tokens utilize closed source hardware for
its security-critical operations.
What makes the Tillitis Key 1 security token unique is that it doesnt
verify applications, it measures them, before running them on its open
hardware security processor.
Each security token contains a Unique Device Secret (UDS),
which together with an application measurement, and an optional
user-provided seed, is used to derive key material unique to each
application. This allows users to build and load their own apps, while
ensuring that each app loaded will have its own cryptographic
identity. The design is similar to TCG DICE. The Tillitis Key 1
platform allows for applications up to 64 KB.
The first implementation is the Tillitis Key 1:
![The Tillitis Key 1 PCB](doc/images/mta1-usb-v1.jpg)
## Documentation
* [System Description](doc/system_description/system_description.md)
* [Threat Model](doc/threat_model/threat_model.md)
* [Framing Protocol](doc/framing_protocol/framing_protocol.md)
* [Boards](hw/boards/README.md)
* [Firmware](hw/application_fpga/fw/mta1_mkdf/README.md)
* [Toolchain setup](doc/toolchain_setup.md)
* [Quickstart](doc/quickstart.md) to program the Tillitis Key1
## About this repository
This repository contains hardware, software and utilities written as
part of the Tillitis Key 1 project. It is structured as monolithic
repository, or "monorepo", where all components live in one
repository.
The repository follows the [OpenTitan
layout](https://docs.opentitan.org/doc/ug/directory_structure/).
## Licensing
See [LICENSES](./LICENSES/README.md) for more information about
the projects' licenses.

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,285 @@
# Framing Protocol
#### Version
* Version: Draft 1.3
* 2021-12-20
## 1 Introduction
This document describes a proposal for a transport level communication
protocol for the mta1_mkdf USB connected secure application device. The
proposal describes the different endpoints, the different levels in the
stack, framing and encoding.
## 2 System description and problem statement
The mta1_mkdf is a USB connected device. The device provides a secure
compute platform and environment for applications providing some service
and function to (the user of) the USB host. Examples of applications
that can be implemented are AUTH token generators, Root of Trust, and
signing oracles.
The mta1_mkdf is implemented using FPGA devices, and the computer
functionality is based on RISC-V. Conceptually, the mta1_mkdf consists
of three levels:
1. The hardware level. The actual FPGA devices and the hardware
implemented in them, for example the RISC-V core, the application and
data memories, but also timers, true random number generators and
hardware access control.
2. The mta1_mkdf firmware and SDK level. The mta1_mkdf contains SW
functionality (called firmware - FW) used to set up the application
environment, but also provide the applications with things like host
communication (the protocol described in this document), key
generation, timers etc.
Similarly, the SDK provides similar convenience functions for the
host side applications. Allowing host side applications to load
applications on the mta1_mkdf, and then communicate with, use the
applications running on the mta1_mkdf.
3. The applications running on the mta1_mkdf, the corresponding host SW.
The hardware, the FW as well as the applications can be endpoints with
which programs on the host may communicate. This means that we need to
be able to address different endpoints in the mta1_mkdf. And, crucially,
the applications and their corresponding host SW may communicate using
custom protocols that are not known today.
This means that we need a general transport mechanism for commands to,
and responses from the endpoints in the mta1_mkdf. Due to the
constrained environment the transport mechanism must be “light”, that is
both easy to implement and to require few resources
### 2.1 mta1_mkdf system description details
The mta1_mkdf consists of two FPGA devices - interface_fpga and
application_fpga.
The interface_fpga contains a USB core and FPGA-local control
functionality (a Finite State Machine - FSM). The FSM is responsible for
sending and receiving bytes to and from the
application_fpga. Additionally, the FSM is the endpoint for commands
directed to the HW in the interface_fpga. This allows the interface_fpga
to provide functionality such as controlling the reset of the
application_fpga, or provide host access to an entropy source.
The application_fpga contains FIFOs and control functionality needed to
receive commands from the host (via the interface_fpga), and send back
responses. The application_fpga also contains a System on Chip (SoC)
with a PicoRV32 CPU core, memories for code and data
storage. Additionally, the SoC includes functionality to provide a
unique device secret (UDS), secure hashing, timers etc. Finally the
application_fpga contains FW stored in read-only memory.
Figure 1 shows the high level architecture that illustrates the
bidirectional data flows between the host and the interface_fpga. And
then the separate command end response flows inside and between the
FPGAs.
![Figure1 shows the architecture with data flows](figure1_architecture.png)
*Figure 1: High level architecture with data flows.*
Note that in the application_fpga it is FW and SW (application) that
acts as endpoints, and they are responsible for interpreting commands
and sending responses.
## 3 Protocol description
The communication is driven by the host and the protocol is
command-response based. The host sends a command, and the mta1_mkdf must
always send a response to a given command. Commands are processed by the
mta1_mkdf in order. If the host sends a new command before receiving a
response to the previous command, it is the responsibility of the host
to determine to which command a received response belongs to.
Commands and responses are sent as frames with a constrained set of possible lengths.
It is the endpoints that are communicating that decides what the data in
the command and response frames mean, and if the commands and responses
are valid and make sense.
### 3.1 Command frame format
A command frame consists of a single header byte followed by one or more
data bytes. The number of data bytes in the frame is given by the header
byte. The header byte also specifies the endpoint for the command.
The bits in the command header byte should be interpreted as:
* Bit [7] (1 bit). Reserved - possible protocol version.
* Bits [6..5] (2 bits). Frame ID tag.
* Bits [4..3] (2 bits). Endpoint number.
0. HW in interface_fpga
1. HW in application_fpga
2. FW in application_fpga
3. SW (application) in application_fpga
* Bit [2] (1 bit). Unused. MUST be zero.
* Bits [1..0] (2 bits). Command data length.
0. 1 byte
1. 4 bytes
2. 32 bytes
3. 128 bytes
Note that the number of bytes indicated by the command data length field
does **not** include the command header byte. This means that a complete
command frame, with a header indicating a data length of 128 bytes, is
129 bytes in length.
Note that the host sets the frame ID tag. The ID tag in a given command
MUST be preserved in the corresponding response to the command.
#### 3.1.1 Command frame examples
Note that these examples mostly don't take into account that the first
byte in the data (following the command header byte) typically is
occupied by the particular app or FW command requested, so there is 1
byte less available for the "payload" of the command.
Some examples to clarify endpoints and commands:
* 0x00: A command to the HW in the interface_fpga with a single byte of
data. The single byte could indicate action such as reading from the
TRNG or resetting the application_fpga.
* 0x13: A command to the FW in the application_fpga with 128 bytes of
data. The data could for example be parts of an application binary to
be loaded into the program memory.
* 0x1a: A command to the application running in the application_fpga
with 32 bytes of data. The data could be a 32 byte challenge to be
signed using a private key derived in the mta1_mkdf.
### 3.2 Response frame format
A response consists of a single header byte followed by one or more bytes.
The bits in the response header byte should be interpreted as:
* Bit [7] (1 bit). Reserved - possible protocol version.
* Bits [6..5] (2 bits). Frame ID tag.
* Bits [4..3] (2 bits). Endpoint number.
0. HW in interface_fpga
1. HW in application_fpga
2. FW in application_fpga
3. SW (application) in application_fpga
* Bit [2] (1 bit). Response status.
0. OK
1. Not OK (NOK)
* Bits [1..0] (2 bits). Response data length.
0. 1 byte
1. 4 bytes
2. 32 bytes
3. 128 bytes
Note that the number of bytes indicated by the response data length field
does **not** include the response header byte. This means that a complete
response frame, with a header indicating a data length of 128 bytes, is
129 bytes in length.
Note that the ID in a response MUST be the same ID as was present in the
header of the command being responded to.
#### 3.2.1 Response frame examples
Note that these examples mostly don't take into account that the first
byte in the data (following the response header byte) typically is
occupied by the particular app or FW response code, so there is 1 byte
less available for the "payload" of the response.
* 0x01: A successful command to the HW in the interface_fpga, which
responds with four bytes of data. For example the interface_fpga
VERSION string.
* 0x14: An unsuccessful command to the FW in the application_fpga which
responds with a single byte of data.
* 0x1b: A successful command to the application running in the
application_fpga. The response contains 128 bytes of data, for example
an EdDSA Ed25519 signature.
### 3.3 Command frame parsing and transfer
Commands are sent via USB to the interface_fpga. The Commands are
buffered in the interface_fpga FIFO until the transfer_agent FSM is able
to transfer the command to the correct endpoint (see Figure 1). To this
end, the command header is parsed by the FSM in the interface_fpga to
determine if the endpoint is in the interface_fpga itself, or if the
command should be transferred to the application_fpga. Commands for the
interface_fpga are processed by HW-functionality in the interface_fpga.
For commands to be transferred to the application, the transfer engine
in the interface_fpga will interrogate the status of the
application_fpga to determine that it can receive one or more bytes. If
the application_fpga is capable of receiving the bytes, the transfer
agent sends over the command bytes including the command byte to the
application_fpga.
### 3.3 Response frame parsing and transfer
HW in the interface FPGA is responsible for detecting responses from any
of the endpoints. If an endpoint has a response, the HW in the
interface_fpga will extract the response and send it to the USB
interface for delivery to the host. The HW in the interface_fpga will
parse the response header bytes to determine how many bytes to expect.
For responses from the application_fpga, the HW in the interface_fpga
will detect available bytes. When the HW in the interface_fpga is ready
to send a response, it will extract bytes from the application_fpga and
send them to the USB interface.
## 4 Inter-FPGA interface functionality
There are two communication directions between the FPGAs - with commands
from the interface_fpga to the application_fpga, and with responses from
the application_fpga to the interface_fpga. The important thing to note
is that the directions are operated independently from each other.
### 4.1 transfer engines
The line interfaces used for each direction are identical and use the
same design. But the direction and thus the transmitter and receiver are
instantiated differently for each direction. This means that each FPGA
contains one tx_engine and one rx_engine.
For commands the cmd_tx_engine is located in the interface_fpga, and the
cmd_rx_engine is located in the application_fpga. For responses the
response_tx_engine is located in the application_fpga, and the
response_rx_engine is located in the interface_fpga.
### 4.2 Line interface
The line interface is a synchronous, byte oriented
interface. Communication is driven by the tx_engine. Communication
starts when the tx_engine has a byte to send, and the rx_engine
indicates that it can receive a byte. The tx_engine sets the tx_en
signal, starts running the tx_clk and shifts out the data bits in the
byte to be transmitted. When all bits have been sent, the tx_engine must
drop the en_signal for a cycle and check if the rx_engine is ready to
receive a new byte.
Bits on the tx_data lines are updated by the tx_engine on the positive
edge of the tx_clk. Bits are sampled by the rx_engine on the negative
edge of the tx_clk. Bits are sent MSB first. The minimum number of
wires, with a single tx_data wire is four. With two bit wide tx_data,
the number of wires is five. This requires a total of 10 wires between
the FPGAs. Figure 2 shows the ports including direction for the
tx_engine and the rx_engine.
![Figure1 shows the engines with their connections](figure2_engines.png)
*Figure 2: The tx_engine and the rx_engine with connections.*
## 5. References
To Be Written.

BIN
doc/images/mta1-usb-dev.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

BIN
doc/images/mta1-usb-v1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

44
doc/quickstart.md Normal file
View File

@ -0,0 +1,44 @@
This document describes how to build the FPGA bitstream, including the
firmware, and get this programmed onto the flash of the Tillitis Key1
USB device.
The Tillitis Key1 kit includes:
- Tillitis Key1 USB device, marked MTA1-USB V1
- Programmer board based on Raspberry Pi Pico, with a white device
holder/jig
- USB-cable with micro-B plug, for connecting the programmer to
computer
- USB-C cable
- USB-C to USB-A adapter
Connect the programmer to the computer using the mentioned cable. It
is convenient to connect the USB device to the USB-C cable, and then
connect the cable to the computer. The latter using the USB-C-to-A, if
needed.
`lsusb` should list two new devies: `cafe:4004 Blinkinlabs ICE40 programmer`
and `1207:8887 Tillitis MTA1-USB-V1`.
The USB device is then placed correctly in the programming jig, and
the hatch closed. The USB device can remain in the jig during repeated
programming and testing cycles. The jig has a cutout to allow for
touching next to the LED where the touch sensor is located.
To install the software needed for building and programming, please
refer to [toolchain_setup.md](toolchain_setup.md).
You are now ready to generate the FPGA bitstream including the standard
firmware, and program the flash on the connected USB device. This should be run
as your regular non-root user, but the the programming is done (in the
Makefile) with `sudo tillitis-iceprog` (so sudo is expected be set up).
```
$ git clone https://github.com/tillitis/tillitis-key1
$ cd tillitis-key1/hw/application_fpga
$ make prog_flash
```
Your Key1 device should eventually be running the firmware with the LED
flashing white, indicating that it is ready to receive an app.

View File

@ -0,0 +1,105 @@
# Address map breakdown and scheme and data formats
## Introduction
We need to agree and define how we use the 32-bit address space. In a way that is:
1. Efficient for HW implementation (decoding logic)
2. Easy to implement in QEMU model
3. Natural, easy to understand and use in SW and develop for
4. Allow simple access separation, control based on security state (FW, APP)
This shared document (hopefully) allows us to accomplish these goals.
## Addressing and core access
In general, the cores only operates on 32-bit words. They expect to get 32-bits
when written to, and return 32-bits when read. The APIs are very simplistic and
does not support byte or half words operations (this would require additional
wires from the CPU core to signal which load-store operation it is trying to
perform - byte, half word, word.
The RISC-V architecture in contrast has byte addressable addresses. This means
that 0x00000000 .. 0x00000003 points to the four different bytes in word 0.
In order to reduce confusion we should:
- Skip the two LSBs in the address. Since the cores have 8 bit address space,
their address space as seen by the CPU is 10 bits, but will be 0x000, 0x004,
0x008, 0x00c etc. A core always sees an 8-bit address. It is the way the core
is hooked up in the application_fpga that controls which 8 bits those are (of
the 32-bit address the CPU requests).
- Always use 32-bit read or write instructions when accessing core registers.
That is lw, sw and use uint32_t as source or destination data type. Using lh,
lb will cause confusion.
Looking at the address format above I would suggest something like:
```
31st bit 0th bit
v v
0000 0000 0000 0000 0000 0000 0000 0000
- Bits [31 .. 30] (2 bits): Top level area prefix (0b00:ROM, 0b01:RAM, 0b10:reserved, 0b11:MMIO)
- Bits [29 .. 24] (6 bits): Core select. We want to support at least 16 cores
- Bits [23 .. 0] (24 bits): Memory/in-core address.
```
This leaves 24 bits for memory or in-core address. Actually, for memory it is
possible to use 30 bits, because core select is not needed.
The cores only have 8-bit addresses, but they are 32-bit word-aligned,
so they will occupy 10 LSBs (also see above).
Assigned top level prefixes:
```
ROM 0b00, 30-bit address
RAM 0b01, 30-bit address
reserved 0b10
MMIO 0b11, 6 bits for core select, 24 bits rest
```
Assigned core prefixes:
```
ROM 0x00
RAM 0x40
TRNG 0xc0 // cores from 0b11000000...
TIMER 0xc1
UDS 0xc2
UART 0xc3
TOUCH 0xc4
MTA1 0xff
```
Examples:
```
0xc3000004: NAME1 in UART
0xff000000: NAME0 in MTA1
0xff000008: VERSION in MTA1
```
## Endianness
Currently all cores handles words in Big Endian format. I need to ensure that
the cores are can handle little endian words. This will probably affect, force
update of which bits are used in status, configuration and control registers.
## Mirror effects
Since each core (and ROM, RAM) only use lower subset of the allocated 24 bit
address, contents will be mirrored modulo the size of the real address. For
example, the TRNG will appear every 10 bit address.
The reason for this is that there is no boundary checks on the address for
in-core addresses. This would require 24 bit comparators. This could be added
when we see that we have resources to do so. A later stage cleanup.
---

View File

@ -0,0 +1,246 @@
# NOTE: this document is outdated, an update is pending.
# MTA1-MKDF software
## Definitions
* Firmware -- software that is part of ROM, and is supplied via the
FPGA bit stream
* Secure Application (short: Application) -- software supplied by
the host machine, which is received, measured, and loaded by the
firmware.
## Types
The PicoRV32 is a 32-bit RISC-V system. All types are little-endian.
## Constraints
The application FPGA is a Lattice UP5K, with the following
specifications:
* 32KB x 4 SPRAM => 128KB for Application
* 4Kb x 30 EBR => 120Kb, PicoRV32 uses ~4 EBR internally => 13KB for
Firmware. We should probably aim for less; 8KB should be the
target.
## Introduction
The MTA1_MKDF has two modes of operation; firmware/loader mode and
application mode. The firmware mode has the responsibility of receive,
measure, and load the application.
The firmware and application uses a memory mapped IO for SoC
communication. The memory map resides at `0x9000_0000`. The
application has a constrained variant of the firmware memory map,
which is outlined below. E.g. UID and UDA are not readable, and the
`APP_{ADDR, SIZE}` are not writable for the application.
The MTA1_MKDF software communicates to the host via the `{RX,TX}_FIFO`
registers, using the framing protocol described in [Framing
Protocol](../framing_protocol/framing_protocol.md).
The firmware defines a protocol (command/response interface) on top of
the framing layer, which is used to bootstrap the application onto the
device.
On the framing layer, it's required that each frame the device
receives, a responding frame must be sent back to the host, in a
ping-pong manner.
Applications define a per-application protocol, which is the contract
between the host and the device.
## Firmware
The firware is part of FPGA bitstream (ROM), and is loaded at
`0x0000_1000`.
### Reset
The PicoRV32 executes `_start` from `crt0.S` `.text` at `0x0000_1000`,
which initializes the stack, `.data`, and `.bss` at
`0x8000_0000`. When the initialization is finished, the firmware waits
for incoming commands from the host, by busy-polling the
`RX_FIFO_{AVAILABLE,DATA}`registers. When a complete command is read,
the firmware executes the command.
### Loading an application
The purpose of the firmware is to bootstrapping an application.
1. The host sends a raw binary, targeted to be loaded at
`0x8000_0000` in the device. The host starts off by sending the
binary size using the `FW_CMD_LOAD_APP_SIZE` command.
2. The firmware executes `FW_CMD_LOAD_APP_SIZE` command, which
stores the application size into `APP_SIZE`, and sets `APP_ADDR`
to zero. A `FW_RSP_LOAD_APP_SIZE` reponse is sent back to the
host, with the status of the action (ok/fail).
3. If the the host receive a sucessful command, it will send
multiple `FW_CMD_LOAD_APP_DATA` commands, containing the full
application.
4. For each received `FW_CMD_LOAD_APP_DATA` commands, the firmware
measures (XXX define how blake2s is used) the data, and places it
into `0x8000_0000`. The firmware response with
`FW_RSP_LOAD_APP_DATA` response to the host for each received
block.
5. When the final block of the application image is received,
`0x8000_0000` is written to `APP_ADDR`. The `CDI` is computed by
used the `UDS` and measurement from the application, and placed
in the `CDI` register. The final `FW_RSP_LOAD_APP_DATA` response
is sent to the host, completing the loading.
NOTE: The firmware uses SPRAM for data and stack. We need to make sure
that the application image does not overwrite the firmware's running
state. The application should probably do a similar relocation for
stack/data at reset, as the firmware does. Further; the firmware need
to check application image is sane. The shared firmware data area
(e.g. `.data` and the stack must be cleared prior launching the
application.
### Starting an application
Starting an application includes the "switch to application mode"
step, which is done by writing to the `SWITCH_APP` regiester. The
switch from firmware mode to application mode is a mode switch, and
context switch. Enter application mode, means the the MMIO region is
restricted; E.g. some registers are removed (`UDS`), and some are
switched from read/write to read-only. This is outlined in the memory
map below.
There is no other means of getting back from application mode to
firmware mode, than resetting/power cycling the device.
Prerequisites: `APP_SIZE` and `APP_ADDR` has to be non-zero.
1. The host sends `FW_CMD_RUN_APP` to the device.
2. The firmware respons with `FW_RSP_RUN_APP`
3. The firmware writes a non-zero to `SWITCH_APP`, and executes
```
// a0 = 0x9000_0000 + 0x420 (APP_ADDR address)
lw a0,1056(a0)
jalr x0,0(a0)
```
4. The device is now in application mode, and executes the code from
`0x8000_0000`.
### Protocol definition
Available commands/reponses:
#### `FW_{CMD,RSP}_LOAD_APP_SIZE`
#### `FW_{CMD,RSP}_LOAD_APP_DATA`
#### `FW_{CMD,RSP}_RUN_APP`
#### `FW_{CMD,RSP}_NAME_VERSION`
#### `FW_{CMD,RSP}_UID`
#### `FW_{CMD,RSP}_TRNG_DATA`
#### `FW_{CMD,RSP}_TRNG_STATUS`
#### `FW_{CMD,RSP}_VERIFY_DEVICE`
Verification that the device is an authentic Mullvad
device. Implemented using challenge/response.
#### `FW_{CMD,RSP}_GET_APPLICATION_DIGEST`
This command returns the un-keyed hash digest for the application that
was loaded. It allows the host to verify that the application was
correctly loaded. This means that the CDI calculated will be correct
given that the UDS has not been modified.
XXX Should we think a bit more about versioning/possiblity to extend?
Is 1B enough for a command/response range?
#### Get the name and version of the device
```
host ->
u8 CMD[1 + 1];
CMD[0].len = 1 // command frame format
CMD[1] = 0x01 // FW_CMD_NAME_VERSION
host <-
u8 RSP[1 + 32]
RSP[0].len = 33 // command frame format
RSP[1] = 0x02 // FW_RSP_NAME_VERSION
RSP[2..6] = NAME0
RSP[6..10] = NAME1
RSP[10..14] = VERSION
RSP[14..] = 0
```
#### Load an application
```
host ->
u8 CMD[1 + 32];
CMD[0].len = 5 // command frame format
CMD[1] = 0x03 // FW_CMD_LOAD_APP_SIZE
CMD[2..6] = APP_SIZE
CMD[6..] = 0
host <-
u8 RSP[1 + 4];
RSP[0].len = 5 // command frame format
RSP[1] = 0x04 // FW_RSP_LOAD_APP_SIZE
RSP[2] = STATUS
RSP[3..] = 0
repeat ceil(APP_SIZE / 63) times:
host ->
u8 CMD[1 + 64];
CMD[0].len = 65 // command frame format
CMD[1] = 0x05 // FW_CMD_LOAD_APP_DATA
CMD[2..] = APP_DATA (pad with zeros)
host <-
u8 RSP[1 + 4]
RSP[0].len = 5 // command frame format
RSP[1] = 0x06 // FW_RSP_LOAD_APP_DATA
RSP[2] = STATUS
RSP[3..] = 0
```
### Memory map
The memory map exposes SoC functionality to the software, when in
firmware mode (privileged mode) It is s set of memory mapped
registers, starting at base address `0x9000_0000`.
| *name* | *r/w* | *offset* | *size* | *type* | *content* | *description* |
|--------------------|-------|----------|--------|---------|-----------|---------------------------------------------------------|
| UDS[^1] | r | 0x0 | 32B | u8[32] | | Unique Device Secret key. |
| UDA | r | 0x20 | 16B | u8[16] | | Unique Device Authentication key. |
| SWITCH_APP | w | 0x30 | 1B | u8 | | Switch to application mode. Write non-zero to trigger. |
| XXX 460 bytes hole | | | | | | |
| UDI | r | 0x200 | 8B | u64 | | Unique Device ID (UDI). |
| NAME0 | r | 0x208 | 4B | char[4] | "mta1" | |
| NAME1 | r | 0x20c | 4B | char[4] | "mkdf" | |
| VERSION | r | 0x210 | 4B | u32 | 1 | Current version. |
| RX_FIFO_AVAILABLE | r | 0x214 | 1B | u8 | | Non-zero if a valid byte can be read from RX_FIFO_DATA. |
| RX_FIFO_DATA | r | 0x215 | 1B | u8 | | FIFO Rx data. |
| TX_FIFO_AVAILABLE | r | 0x216 | 1B | u8 | | Non-zero if a valid byte can be written to TX_FIFO_DATA |
| TX_FIFO_DATA | w | 0x217 | 1B | u8 | | FIFO Tx data. |
| LED | r/w | 0x218 | 4B | u32 | | LED |
| COUNTER | r | 0x21c | 4B | u32 | | Counter |
| TRNG_STATUS | r | 0x220 | 4B | u32 | | data_ready/error |
| TRNG_DATA | r | 0x224 | 4B | u32 | | TRNG data |
| XXX 472 bytes hole | | | | | | |
| CDI | r/w | 0x400 | 32B | u8[32] | | Compound Device Identifier (CDI). UDS+measurement... |
| APP_ADDR | r/w | 0x420 | 4B | u32 | | Application address (0x8000_0000) |
| APP_SIZE | r/w | 0x424 | 4B | u32 | | Application size |
[^1]: The UDS can only be read *once* per power-cycle.
## Application
### Memory map
See the [Memory model](./memory_model.md) for information about the
memory map and how access to memory areas work.

View File

@ -0,0 +1,233 @@
# System Description
## Purpose and Revision
The purpose of this document is to provide a description of the
mta1_mkdf. What it is, what is supposed to be used for, by whom, where
and possible use cases. The document also provides a functional level
description of features and components of the mta1_mkdf.
Finally, the document acts as a requirement description. For the
requirements, the document follows
[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) to indicate
requirement levels.
The described functionality and requirements applies
to version one (v1) of the mta1_mkdf.
The intended users of this document are:
- Implementors of the mta1_mkdf hardware, firmware and SDKs
- Developers of secure applications for the mta1_mkdf
- Technically skilled third parties that wants to understand the
mta1_mkdf
## Introduction
The mta1_mkdf is USB-connected, RISC-V based application platform. The
purpose of the mta1_mkdf is to provide a secure application environment
for applications that provides some security functionality needed by the
user. Some examples of such security functionality are:
- TOTP token generators
- Signing oracles
- SSH login dongles
### Measured Based Security
The key, unique feature of the mta1_mkdf is that it measures the secure
application when the application is being loaded onto the device. The
measurement, combined with a Unique Device Secret (UDS) is used to
derive secrets for the application.
The consequence of this is that if the application is altered, the keys
derived will also change. Conversely, if the keys derived are the same as
last time the application was loaded onto the same device, the
application can be trusted not to have been altered.
Note that since the UDS is per-device unique, the same application
loaded onto another mta1_mkdf device will cause a different set of keys
to be derived. This ties keys to a specific device.
The derivation can also be combined with a User Supplied Secret
(USS). This means that keys derived are both based on something the user
has - the specific device, and something the user knows (the USS). And
the keys are protected and can be trusted because of the measurement
being used in the derivation.
### Assets
The mta1_mkdf store and use the following assets internally:
- UDS - Unique Device Secret. Provisioned and stored during
device manufacturing. Never to be replaced during the life time of
a given device. Used to derive application secrets. Must never leave
the device. Mullvad must NOT store a copy of the UDS.
- UDI - Unique Device ID. Provisioned and stored during
device manufacturing. Never to be replaced or altered during the life
time of a given device. May be copied, extracted, read from the device.
- UDA - Unique Device Authentication Secret. Provisioned and stored during
device manufacturing. Never to be replaced during the life time of
a given device. Used to authenticate a specific device. Must never
leave the device. Mullvad MUST have a copy of the UDA.
Additionally the following asset could be provided from the host:
- USS - User Supplied Secret. Provisioned by the application. May
possibly be replaced many times. Supplied from the host to the
device. Should not be revealed to a third party.
### Subsystems and Components
The mta1_mkdf as a project, system and secure application platform
consists of a number of subsystems and components, modules, support
libraries etc. Roughly these can be divided into:
- mta1_mkdf boards. PCB designs for development and general usage
- interface_fpga. FPGA design with cores
- application_fpga. FPGA design with cores including CPU and memory
- application_fpga FW. The base software running on the CPU to boot, load
applications, derive keys etc
- application_fpga secure application. One or more applications loaded
into the application_fpga to provide some functionality to the user of
the host
- host side application loader. Software that talks to the FW in the
application_fpga to load a secure application
- host side boot, management. Support software to boot, authenticate the
mta1_mkdf board connected to a host
- host side secure application. Software that communicates with the
secure application running in the application_fpga as needed to solve
a security objective
- application_fpga FW SDK. Tools, libraries, documentation and examples
to support development of the application_fpga firmware
- secure application SDK. Tools, libraries, documentation and examples
to support development of the secure applications to be loaded onto
the application_fpga
- host side secure application SDK. Tools, libraries, documentation and
examples to support development of the host applications
## Application FPGA Hardware Functionality
The Application FPGA hardware should provide the following:
1. Fixed information
- Unique Device ID (UID)
- 64 bits
- Readable via API before application start
- Generated and stored by Mullvad
- Unique Device Authentication key (UDA)
- At least 128 bits number
- Readable by FW before application start
- Generated and stored by Mullvad
- Unique Device Secret (UDS)
- 256 bits
- Readable by HW before application start
- Generated but NOT stored by Mullvad
- NAME
- 64 bits. ASCII string. "mta1_mkdf"
- Readable via API before application start
- Set by Mullvad as part of FPGA design
- VERSION: version
- 32 bits. 32 bit data, for example 1
- Readable via API before application start
- Set by Mullvad as part of FPGA design
2. Communication
- Rx-FIFO with status (data_available)
- 8 bit data in RX_FIFO_DATA address
- Byte received status bit in RX_FIFO_AVAILABLE address
- Readable by FW and application
- Tx-FIFO with capacity (fifo_ready)
- 8 bit data in TX_FIFO_DATA address
- Ready to store byte status bit in TX_FIFO_READY address
- Status readable by FW and application
- Data writable by FW and application
3. I/O
- LED (RGB)
- Status and control in LED address
- Readable and writable by FW and application
4. Counter
- One general purpose counter
- Prescaler (for counting cycles and seconds)
- Start value, alternatively reset
- Saturating max, alternatively stop at zero
- Readable and writable by FW and application
5. TRNG
- ROSC based internal entropy source
- Von Neumann decorrelation
- Simple self-testing ability
- 32 bit data
- Status (data_ready, error)
- Readable by FW and application
6. Introspection
- Address och size of loaded application
- Readable by FW and application
## Application FPGA Firmware Functionality
The firmware in the application should provide the following
functionality:
- Read access to fixed values:
- application_fpga name and version strings
- Unique Device ID (UID)
- Read and write to test register used for debugging
- Respond to challenge/response based device authentication commands
- Receive and store a 32 byte User Supplied Secret (USS)
- Receive, store and measure a secure application
- Derive Application Master Secret (AMS) given measurement, UDS and USS
- Provide hashing using Blake2s
- Start a loaded application. This includes locking down access to UDS,
UDA etc
## References
More detailed information about the software running on the device
(referred to firmware, SDK, and secure application), can be found in
the [software document](software.md).
## Work in Progress
TODOs and random notes, questions to be worked into the document. Or be
scratched.
- Possible technical solution - Could we reuse the button as a physical
presence detect when injecting a bitstream from the interface_fpga
to the application_fpga? Alternative have a strap, which would
require opening the stick. The stick is the sold with nail polish to
reseal it.
- Ideas - mitigating mechanisms for host bases threats
- Push button
- User Supplied Secret (USS)
- Open Questions to be investigated, handled
- Terminology - naming things
- How to create trust in the SDKs

View File

@ -0,0 +1,250 @@
# Threat model
## Introduction
The mta1_mkdf device is a platform for running secure applications in a
restricted execution environment physically separate from the
device host. The secure applications provide functionality and
controlled access to derived secrets on the device. The purpose of the
device is to solve typical end user authentication problems.
For more information about the mta1_mkdf, please see the [mta1_mkdf
System Description](../system_description/system_description.md).
This document describes the threat model for the mta1_mkdf. Based on the
system description and use cases, the threat model tries to capture and
describe the threats that needs to be mitigated in order for the
mta1_mkdf to meet its purpose and objectives.
## Version information
The threat model applies to version one of the mta1_mkdf. The threat
model will be updated as our knowledge and abilities progress and new
versions are developed.
### Version one
* A publicly available, but limited device targeted for a select set of
friendly, competent users.
* Supports Linux and possibly MacOS as host
* Used for a limited set of use cases, primarily for testing
* Users are expected to:
* Note that a device is missing within 24 hours
* Note that a device has been physically tampered with
* Note that a device behaves in an unexpected way
#### Use cases for Version one
**Time Based OTP (TOTP)**
Used at least once per day. Current time supplied by the host.
1. The user connect the device to the host
2. The user runs SW on the host to load the TOTP application
3. The user provides its User Supplied Secret (USS) via host SW
4. The user triggers a TOTP operation using host SW
5. The user press the button on the device
6. The device returns the calculated TOTP token
7. SW on the host use the TOTP token to perform authentication for an
application or target host
**Ed25519 Signing**
Used at least once per day.
1. The user connect the device to the host
2. The user runs SW on the host to load the Ed25519 application
3. The user triggers an Ed25519 signing operation using host SW. This
also loads the message or hash to be signed
4. The user press the button on the device
5. The device returns the Ed25519 signature
6. SW on the host use the signature for authentication, verification
**SSH connect**
Used at least once per day.
1. The user connect the device to the host
2. The user runs SW on the host to load the SSH auth application
3. The user provides its User Supplied Secret (USS) via host SW
4. The user trigger a SSH connect operation from the host
5. The user press the button on the device
6. The device returns a SSH signature
7. The SSH application on the host use the SSH signature for
authentication to the remote SSH server
### Version N
* Publicly available devices for end users for which there are no
expectation on knowledge or competence beyond normal IT usage skills
* Used in normal IT systems, but also for more sensitive enterprise and
operational use cases
## Assumptions
* There are no backdoors or vulnerabilities in Lattice iCE40 UltraPlus
FPGA devices that allow access to internal configuration memory after
the device has been locked.
* The Project IceStorm toolchain, including YoSys and NextPnR generates
a correct design, and also does not inject hardware exfiltration
mechanisms in the generated bitstream.
* There is no access to the contents of the internal, Non-Volatile
Configuration Memory (NVM) from the FPGA fabric besides the
configuration circuit.
* Toolchain for development of FPGA HW, application_fpga FW does not
contain backdoors etc.
* The design including source code for FPGA, SDK, FW, boot SW, board
design is open and published.
* The end user is not an attacker. The end user at least doesn't
knowingly aid, support the attacker in attacks on its device
## Assets
* UDS - Unique Device Secret. Provisioned and stored during
device manufacturing. Never to be replaced during the life time of
a given device. Used to derive application secrets. Must never leave
the device. Mullvad must NOT store a copy of the UDS.
* USS - User Supplied Secret. Provisioned by the application. May
possibly be replaced many times. Supplied from the host to the
device. Should not be revealed to a third party.
* UDI - Unique Device ID. Provisioned and stored during
device manufacturing. Never to be replaced or altered during the life
time of a given device. May be copied, extracted, read from the device.
* UDA - Unique Device Authentication Secret. Provisioned and stored during
device manufacturing. Never to be replaced during the life time of
a given device. Used to authenticate a specific device. Must never
leave the device. Mullvad MUST have a copy of the UDA.
## Threat Actors - The bad guys
Different actors have different reasons, access to competence, resources
etc. This description tries to capture the possible attacks and attacks
vectors through four synthetic threat actors.
### 0. Average Joe
[Average Joe Soundtrack](https://www.youtube.com/watch?v=BB0DU4DoPP4)
* Curious opportunist
* No real competence, no resources beyond a personal computer
* No planning or preparation before an attack
* Prepared to invest little time (minutes) or resources - for example to
connect a device found, try a few passwords
* End game is to gain access to possible information, resources unknown
to the attacker before the attack is performed
### 1. The CCC Hacker
[CCC Hacker Soundtrack](https://www.youtube.com/watch?v=l8DBEbmPh7E)
* Sympathetic to the goals of the project
* Wants to probe all parts and the system in a quest to determine how
the device really works, use it in possibly different ways, find
weaknesses (and get them fixed).
* Is possibly a user, but in this case not the legitimate end user
* Have a high level of competence
* Prepared to spend time to prepare and perform an attack. Possibly low
effort over an extended period
* Access to compute resources. Possibly access to lab equipment
* Will try all possible SW and HW attack vectors. In and out of scope
* End game is to find flaws in threat model. Acquire knowledge and
findings to produce an interesting talk at CCC, USENIX or Security
Fest
### 2. vERyRevil
[vERyRevil Soundtrack](https://www.youtube.com/watch?v=sTSA_sWGM44)
* Ransomware gang. Driven by short term financial gain
* Short term focus. Fastest possible access to economic assets
* Have, or can acquire high level of competence
* Have access to large amount of resources
* Have time and is prepared to spend time on preparations
* Short time to perform an attack. Will not persist for a long time
* Will do strict cost benefit-analysis to decide to perform, abort
attacks if they don't work
* SW based attacks. Is assumed to remotely own the host
* Supply chain attacks on secure application, host application, SDK,
infiltration of device and application development
* End game is to gain access, control over resources protected by the
device. Resources that can be used as leverage for financial gain
### 4. APT4711
[APT4711 Soundtrack](https://www.youtube.com/watch?v=lrWV6pxepDo)
* State actor
* Interested in access to information, perform surveillance, and
possibly control of the end user or resources
* Long term focus. Attacks are discreet and persistent
* Access to high competence
* Access to very large amounts of resources
* Prepared to invest a lot of time, effort to prepare and execute an
attack
* Prepared to perform physical visits (missions) at target (end user) as
well as Mullvad or Mullvad suppliers in order to manipulate, steal,
replace components, systems
* SW based attacks. Is assumed to remotely own the host
* Supply chain attacks - both on SW and HW, components
* Supply chain attacks on application, host application, SDK,
development
* End game: Long term stealth presence providing access to information
about the end user
## Attacks in Scope
The following attacks are in scope for version one
* All digital attacks from the host including but not limited to:
* The framing protocol and all recipients (endpoints)
* manipulation, fuzzing, injection, reordering and replay of any and
all communication
* Time based side channel attacks on challenge-response device
authentication
* Time based side channel attacks on UDS based key derivation
* Time based side channel attacks on secure applications developed by
Mullvad
* Supply chain physical attacks on devices provisioned by Mullvad
* Decapping and physical probing on the FPGA
* Supply chain attacks on Mullvad provided HW and SW design resources
## Attacks out of scope
The following attacks are out of scope for version one
* Electromagnetic-, power-, and optical-based fault injection attacks
* Electromagnetic-based side channel, differential power analysis and
correlation leakage attacks
## Consequences - Game Over
The following Game Over Scenarios have been identified
* The attacker gains access to the UDS and the USS
* Requires replacement of the device with a new unit
## Work In Progress
TODOs and random notes.
* TODO: Mention and separately describe the NVCM and the CRAM.
* TODO: Mention limitations related to EBR and SPRAM

101
doc/toolchain_setup.md Normal file
View File

@ -0,0 +1,101 @@
# Toolchain setup
Here are instructions for setting up the tools required to build the project.
Tested on Ubuntu 22.04 LTS.
## Gateware: icestorm toolchain
These steps are used to build and install the
[icestorm](http://bygone.clairexen.net/icestorm/) toolchain (in
`/usr/local`). Note that nextpnr replaces Arachne-PNR.
sudo apt install build-essential clang lld bison flex libreadline-dev \
gawk tcl-dev libffi-dev git mercurial graphviz \
xdot pkg-config python3 libftdi-dev \
python3-dev libboost-dev libeigen3-dev \
libboost-dev libboost-filesystem-dev \
libboost-thread-dev libboost-program-options-dev \
libboost-iostreams-dev cmake
git clone https://github.com/YosysHQ/icestorm
cd icestorm
make -j$(nproc)
sudo make install
cd ..
# Custom iceprog for the RPi 2040-based programmer (will be upstreamed).
git clone -b interfaces https://github.com/tillitis/icestorm tillitis--icestorm
cd tillitis--icestorm/iceprog
make
sudo make PROGRAM_PREFIX=tillitis- install
cd ../..
git clone https://github.com/YosysHQ/yosys
cd yosys
# Avoiding current issue with yosys & icebram, filed in:
# https://github.com/YosysHQ/yosys/issues/3478
git checkout 06ef3f264afaa3eaeab45cc0404d8006c15f02b1
make -j$(nproc)
sudo make install
cd ..
git clone https://github.com/YosysHQ/nextpnr
cd nextpnr
cmake -DARCH=ice40 -DCMAKE_INSTALL_PREFIX=/usr/local .
make -j$(nproc)
sudo make install
References:
* http://bygone.clairexen.net/icestorm/
## Firmware: riscv toolchain
The Tillitis Key 1 implements a
[picorv32](https://github.com/YosysHQ/picorv32) soft core CPU, which
is a RISC-V microcontroller with the M and C instructions (RV32IMC).
You can read
[more](https://www.sifive.com/blog/all-aboard-part-1-compiler-args)
about it.
The project uses the LLVM/Clang suite, where version 14 is the latest
stable (as of writing). Usually the LLVM/Clang packages that are part
of your distro will work, if not, there are installations instructions
for "Install (stable branch)" at https://apt.llvm.org/ for Debian and
Ubuntu.
References:
* https://github.com/YosysHQ/picorv32
## Optional
These tools are used for specific sub-components of the project, and
are not required for general development
### Kicad 6.0: Circuit board designs
The circuit board designs were all created in [KiCad
6.0](https://www.kicad.org/).
### mta1-usb-v1-programmer: RPi 2040 toolchain
These tools are needed to build the programmer firmware for the
mta1-usb-v1-programmer
TODO
* source code: https://github.com/Blinkinlabs/ice40_flasher
### mta1-usb-v1: ch552 USB to Serial firmware
The USB to Serial firmware runs on the CH552 microcontroller, and
provides a USB CDC profile which should work with the default drivers
on all major operating systems.
TODO
References:
* source code: https://github.com/tillitis/tillitis-key1/tree/main/hw/boards/mta1-usb-v1/ch552_fw
* Compiler: [SDCC](http://sdcc.sourceforge.net/)
* Library: https://github.com/Blinkinlabs/ch554_sdcc
* Flashing tool: https://github.com/ole00/chprog

View File

@ -0,0 +1,291 @@
#=======================================================================
#
# Makefile
# --------
# Makefile for building, simulating, running all application_fpga
# HW targets as well as its firmware.
#
#
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#=======================================================================
#-------------------------------------------------------------------
# Defines.
#-------------------------------------------------------------------
SHELL := /bin/bash
CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
P := $(CUR_DIR)
YOSYS_PATH ?=
NEXTPNR_PATH ?=
ICESTORM_PATH ?=
# Size in 32-bit words
BRAM_FW_SIZE ?= 2048
PIN_FILE ?= application_fpga_mta1_usb_v1.pcf
SIZE ?= llvm-size-14
OBJCOPY ?= llvm-objcopy-14
CC = clang-14
CFLAGS = -target riscv32-unknown-none-elf -march=rv32imc -mabi=ilp32 \
-static -std=gnu99 -O2 -ffast-math -fno-common -fno-builtin-printf \
-fno-builtin-putchar -nostdlib -mno-relax -Wall -flto
AS = clang-14
ASFLAGS = -target riscv32-unknown-none-elf -march=rv32imc -mabi=ilp32 -mno-relax
ICE40_SIM_CELLS = $(shell yosys-config --datdir/ice40/cells_sim.v)
# FPGA source files.
TOP_SRC = $(P)/rtl/application_fpga.v
VERILATOR_TOP_SRC = $(P)/tb/application_fpga_vsim.v
VERILOG_SRCS = \
$(P)/rtl/reset_gen.v \
$(P)/rtl/ram.v \
$(P)/rtl/rom.v \
$(P)/core/picorv32/rtl/picorv32.v \
$(P)/core/timer/rtl/timer_core.v \
$(P)/core/timer/rtl/timer.v \
$(P)/core/uds/rtl/uds.v \
$(P)/core/touch_sense/rtl/touch_sense.v \
$(P)/core/mta1/rtl/mta1.v \
$(P)/core/uart/rtl/uart_core.v \
$(P)/core/uart/rtl/uart_fifo.v \
$(P)/core/uart/rtl/uart.v \
$(P)/core/trng/rtl/firo.v \
$(P)/core/trng/rtl/garo.v \
$(P)/core/trng/rtl/figaro_core.v \
$(P)/core/trng/rtl/figaro.v
FIRMWARE_DEPS = \
$(P)/fw/mta1_mkdf_mem.h \
$(P)/fw/mta1_mkdf/types.h \
$(P)/fw/mta1_mkdf/lib.h \
$(P)/fw/mta1_mkdf/proto.h
FIRMWARE_OBJS = \
$(P)/fw/mta1_mkdf/main.o \
$(P)/fw/mta1_mkdf/start.o \
$(P)/fw/mta1_mkdf/proto.o \
$(P)/fw/mta1_mkdf/lib.o \
$(P)/fw/mta1_mkdf/blake2s/blake2s.o
TESTFW_OBJS = \
$(P)/fw/testfw/main.o \
$(P)/fw/mta1_mkdf/start.o \
$(P)/fw/mta1_mkdf/proto.o \
$(P)/fw/mta1_mkdf/lib.o
#-------------------------------------------------------------------
# All: Complete build of HW and FW.
#-------------------------------------------------------------------
all: application_fpga.bin
.PHONY: all
#-------------------------------------------------------------------
# The size_mismatch target make sure that we don't end up with an
# incorrect BRAM_FW_SIZE
# -------------------------------------------------------------------
size_mismatch: firmware.elf
@test $$($(SIZE) $< | awk 'NR==2{print $$4}') -le $$(( 32 / 8 * $(BRAM_FW_SIZE) )) || \
(echo "The 'BRAM_FW_SIZE' variable needs to be increased" && false)
.PHONY: size_mismatch
#-------------------------------------------------------------------
# Firmware generation.
# Included in the bitstream.
#-------------------------------------------------------------------
LDFLAGS=-T $(P)/fw/mta1_mkdf/firmware.lds
$(FIRMWARE_OBJS): $(FIRMWARE_DEPS)
$(TESTFW_OBJS): $(FIRMWARE_DEPS)
firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/mta1_mkdf/firmware.lds
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@
testfw.elf: $(TESTFW_OBJS) $(P)/fw/mta1_mkdf/firmware.lds
$(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@
# Generate a fake BRAM file that will be filled in later after place-n-route
bram_fw.hex:
$(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@
firmware.hex: firmware.bin size_mismatch
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
testfw.hex: testfw.bin size_mismatch
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
%.bin: %.elf
$(SIZE) $<
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $< $@
chmod -x $@
#-------------------------------------------------------------------
# Source linting.
#-------------------------------------------------------------------
LINT=verilator
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME \
--timescale 1ns/1ns -DNO_ICE40_DEFAULT_ASSIGNMENTS
lint: $(TOP_SRC) $(VERILOG_SRCS) $(ICE40_SIM_CELLS)
$(LINT) $(LINT_FLAGS) \
-DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
-DFIRMWARE_HEX=\"$(P)/firmware.hex\" \
-DUDS_HEX=\"$(P)/data/uds.hex\" \
-DUDI_HEX=\"$(P)/data/udi.hex\" \
--top-module application_fpga $^
.PHONY: lint
#-------------------------------------------------------------------
# Build Verilator compiled simulation for the design.
#-------------------------------------------------------------------
verilator: $(VERILATOR_TOP_SRC) $(VERILOG_SRCS) firmware.hex $(ICE40_SIM_CELLS) \
$(P)/tb/application_fpga_verilator.cc
verilator --timescale 1ns/1ns -DNO_ICE40_DEFAULT_ASSIGNMENTS \
-Wall -Wno-COMBDLY -Wno-lint \
-DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
-DFIRMWARE_HEX=\"$(P)/firmware.hex\" \
-DUDS_HEX=\"$(P)/data/uds.hex\" \
-DUDI_HEX=\"$(P)/data/udi.hex\" \
--cc --exe --Mdir verilated --top-module application_fpga \
$(filter %.v, $^) $(filter %.cc, $^)
make -C verilated -f Vapplication_fpga.mk
.PHONY: verilator
#-------------------------------------------------------------------
# Main FPGA build flow.
# Synthesis. Place & Route. Bitstream generation.
#-------------------------------------------------------------------
synth.json: $(TOP_SRC) $(VERILOG_SRCS) bram_fw.hex
$(YOSYS_PATH)yosys -v3 -l synth.log -DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
-DFIRMWARE_HEX=\"$(P)/bram_fw.hex\" \
-DUDS_HEX=\"$(P)/data/uds.hex\" \
-DUDI_HEX=\"$(P)/data/udi.hex\" \
-p 'synth_ice40 -dsp -top application_fpga -json $@; write_verilog -attr2comment synth.v' \
$(filter %.v, $^)
application_fpga.asc: synth.json $(P)/data/$(PIN_FILE)
$(NEXTPNR_PATH)nextpnr-ice40 --ignore-loops --up5k --package sg48 --json $< \
--pcf $(P)/data/$(PIN_FILE) --asc $@
application_fpga.bin: application_fpga.asc bram_fw.hex firmware.hex
$(ICESTORM_PATH)icebram -v bram_fw.hex firmware.hex < $< > $<.tmp
$(ICESTORM_PATH)icepack $<.tmp $@
@-$(RM) $<.tmp
application_fpga_testfw.bin: application_fpga.asc bram_fw.hex testfw.hex
$(ICESTORM_PATH)icebram -v bram_fw.hex testfw.hex < $< > $<.tmp
$(ICESTORM_PATH)icepack $<.tmp $@
@-$(RM) $<.tmp
#-------------------------------------------------------------------
# post-synthesis functional simulation.
#-------------------------------------------------------------------
synth_tb.vvp: $(P)/tb/tb_application_fpga.v synth.json
iverilog -o $@ -s tb_application_fpga synth.v $(P)/tb/tb_application_fpga.v \
-DNO_ICE40_DEFAULT_ASSIGNMENTS $(ICE40_SIM_CELLS)
chmod -x $@
synth_sim: synth_tb.vvp
vvp -N $<
.PHONY: synth_sim
synth_sim_vcd: synth_tb.vvp
vvp -N $< +vcd
.PHONY: synth_sim_vcd
#-------------------------------------------------------------------
# post-place and route functional simulation.
#-------------------------------------------------------------------
route.v: application_fpga.asc $(P)/data/$(PIN_FILE)
icebox_vlog -L -n application_fpga -sp $(P)/data/$(PIN_FILE) $< > $@
route_tb.vvp: route.v tb/tb_application_fpga.v
iverilog -o $@ -s tb_application_fpga $^ $(ICE40_SIM_CELLS)
chmod -x $@
route_sim: route_tb.vvp
vvp -N $<
.PHONY: route_sim
route_sim_vcd: route_tb.vvp
vvp -N $< +vcd
.PHONY: route_sim_vcd
#-------------------------------------------------------------------
# FPGA device programming.
#-------------------------------------------------------------------
prog_flash: application_fpga.bin
sudo tillitis-iceprog $<
.PHONY: prog_flash
prog_flash_testfw: application_fpga_testfw.bin
sudo tillitis-iceprog $<
.PHONY: prog_flash_testfw
#-------------------------------------------------------------------
# Post build analysis.
#-------------------------------------------------------------------
timing: application_fpga.asc $(P)/data/$(PIN_FILE)
$(ICESTORM_PATH)icetime -c 12 -tmd up5k -P sg48 -p $(P)/data/$(PIN_FILE) -t $<
view: tb_application_fpga_vcd
gtkwave $< application_fpga.gtkw
#-------------------------------------------------------------------
# Cleanup.
#-------------------------------------------------------------------
clean: clean_fw
rm -f bram_fw.hex
rm -f synth.{log,v,json} route.v application_fpga.{asc,bin,vcd} application_fpga_testfw.bin
rm -f tb_application_fpga.vvp synth_tb.vvp route_tb.vvp
rm -f *.vcd
rm -rf verilated
rm -f tools/tpt/*.hex
rm -rf tools/tpt/__pycache__
.PHONY: clean
clean_fw:
rm -f firmware.{elf,elf.map,bin,hex}
rm -f $(FIRMWARE_OBJS)
rm -f testfw.{elf,elf.map,bin,hex}
rm -f $(TESTFW_OBJS)
.PHONY: clean_fw
#-------------------------------------------------------------------
# Display info about targets.
#-------------------------------------------------------------------
help:
@echo ""
@echo "Build system for application_fpga FPGA design and firmware."
@echo ""
@echo "Supported targets:"
@echo "------------------"
@echo "all Build all targets."
@echo "firmware.elf Build firmware ELF file."
@echo "firmware.hex Build firmware converted to hex, to be included in bitstream."
@echo "bram_fw.hex Build a fake BRAM file that will be filled in later after place-n-route."
@echo "verilator Build Verilator simulation program"
@echo "lint Run lint on Verilog source files."
@echo "prog_flash Program device flash with FGPA bitstream including firmware (using the RPi Pico-based programmer)."
@echo "prog_flash_testfw Program device flash as above, but with testfw."
@echo "clean Delete all generated files."
@echo "clean_fw Delete only generated files for firmware. Useful for fw devs."
#=======================================================================
# EOF Makefile
#=======================================================================

View File

@ -0,0 +1,4 @@
# mta1
## Introduction
Top level core that provides chip info, debug support and chip level control.

View File

@ -0,0 +1,326 @@
//======================================================================
//
// mta1.v
// ------
// Top level information, debug and control core for the mta1 design.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module mta1(
input wire clk,
input wire reset_n,
output wire fw_app_mode,
output wire led_r,
output wire led_g,
output wire led_b,
input wire gpio1,
input wire gpio2,
output wire gpio3,
output wire gpio4,
input wire cs,
input wire we,
input wire [7 : 0] address,
input wire [31 : 0] write_data,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_SWITCH_APP = 8'h08;
localparam ADDR_LED = 8'h09;
localparam LED_R_BIT = 2;
localparam LED_G_BIT = 1;
localparam LED_B_BIT = 0;
localparam ADDR_GPIO = 8'h0a;
localparam GPIO1_BIT = 0;
localparam GPIO2_BIT = 1;
localparam GPIO3_BIT = 2;
localparam GPIO4_BIT = 3;
localparam ADDR_APP_START = 8'h0c;
localparam ADDR_APP_SIZE = 8'h0d;
localparam ADDR_DEBUG = 8'h10;
localparam ADDR_CDI_FIRST = 8'h20;
localparam ADDR_CDI_LAST = 8'h27;
localparam ADDR_UDI_FIRST = 8'h30;
localparam ADDR_UDI_LAST = 8'h31;
localparam MTA1_NAME0 = 32'h6d746131; // "mta1"
localparam MTA1_NAME1 = 32'h6d6b6466; // "mkdf"
localparam MTA1_VERSION = 32'h00000004;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg [31 : 0] cdi_mem [0 : 7];
reg [31 : 0] cdi_mem_we;
reg [31 : 0] udi_mem [0 : 1];
initial $readmemh(`UDI_HEX, udi_mem);
reg switch_app_reg;
reg switch_app_we;
reg [2 : 0] led_reg;
reg led_we;
reg [1 : 0] gpio1_reg;
reg [1 : 0] gpio2_reg;
reg gpio3_reg;
reg gpio3_we;
reg gpio4_reg;
reg gpio4_we;
reg [31 : 0] app_start_reg;
reg app_start_we;
reg [31 : 0] app_size_reg;
reg app_size_we;
reg [31 : 0] debug_reg;
reg debug_we;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
/* verilator lint_off UNOPTFLAT */
reg [31 : 0] tmp_read_data;
reg tmp_ready;
/* verilator lint_on UNOPTFLAT */
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = tmp_ready;
assign fw_app_mode = switch_app_reg;
assign gpio3 = gpio3_reg;
assign gpio4 = gpio4_reg;
//----------------------------------------------------------------
// Module instance.
//----------------------------------------------------------------
SB_RGBA_DRV #(
.CURRENT_MODE("0b1"), // half-current mode
.RGB0_CURRENT("0b000001"), // 2 mA
.RGB1_CURRENT("0b000001"), // 2 mA
.RGB2_CURRENT("0b000001") // 2 mA
) RGBA_DRV (
.RGB0(led_r),
.RGB1(led_g),
.RGB2(led_b),
.RGBLEDEN(1'h1),
.RGB0PWM(led_reg[LED_R_BIT]),
.RGB1PWM(led_reg[LED_G_BIT]),
.RGB2PWM(led_reg[LED_B_BIT]),
.CURREN(1'b1)
);
//----------------------------------------------------------------
// reg_update
//----------------------------------------------------------------
always @ (posedge clk)
begin : reg_update
if (!reset_n) begin
switch_app_reg <= 1'h0;
led_reg <= 3'h6;
gpio1_reg <= 2'h0;
gpio2_reg <= 2'h0;
gpio3_reg <= 1'h0;
gpio4_reg <= 1'h0;
app_start_reg <= 32'h0;
app_size_reg <= 32'h0;
debug_reg <= 32'h0;
cdi_mem[0] <= 32'h0;
cdi_mem[1] <= 32'h0;
cdi_mem[2] <= 32'h0;
cdi_mem[3] <= 32'h0;
cdi_mem[4] <= 32'h0;
cdi_mem[5] <= 32'h0;
cdi_mem[6] <= 32'h0;
cdi_mem[7] <= 32'h0;
end
else begin
gpio1_reg[0] <= gpio1;
gpio1_reg[1] <= gpio1_reg[0];
gpio2_reg[0] <= gpio2;
gpio2_reg[1] <= gpio2_reg[0];
if (switch_app_we) begin
switch_app_reg <= 1'h1;
end
if (led_we) begin
led_reg <= write_data[2 : 0];
end
if (gpio3_we) begin
gpio3_reg <= write_data[GPIO3_BIT];
end
if (gpio4_we) begin
gpio4_reg <= write_data[GPIO4_BIT];
end
if (app_start_we) begin
app_start_reg <= write_data;
end
if (app_size_we) begin
app_size_reg <= write_data;
end
if (debug_we) begin
debug_reg <= write_data;
end
if (cdi_mem_we) begin
cdi_mem[address[2 : 0]] <= write_data;
end
end
end // reg_update
//----------------------------------------------------------------
// api
//----------------------------------------------------------------
always @*
begin : api
switch_app_we = 1'h0;
led_we = 1'h0;
gpio3_we = 1'h0;
gpio4_we = 1'h0;
app_start_we = 1'h0;
app_size_we = 1'h0;
debug_we = 1'h0;
cdi_mem_we = 1'h0;
cdi_mem_we = 1'h0;
tmp_read_data = 32'h00000000;
tmp_ready = 1'h0;
if (cs) begin
tmp_ready = 1'h1;
if (we) begin
if (address == ADDR_SWITCH_APP) begin
switch_app_we = 1'h1;
end
if (address == ADDR_LED) begin
led_we = 1'h1;
end
if (address == ADDR_GPIO) begin
gpio3_we = 1'h1;
gpio4_we = 1'h1;
end
if (address == ADDR_APP_START) begin
if (!switch_app_reg) begin
app_start_we = 1'h1;
end
end
if (address == ADDR_APP_SIZE) begin
if (!switch_app_reg) begin
app_size_we = 1'h1;
end
end
if (address == ADDR_DEBUG) begin
debug_we = 1'h1;
end
if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin
if (!switch_app_reg) begin
cdi_mem_we = 1'h1;
end
end
end
else begin
if (address == ADDR_NAME0) begin
tmp_read_data = MTA1_NAME0;
end
if (address == ADDR_NAME1) begin
tmp_read_data = MTA1_NAME1;
end
if (address == ADDR_VERSION) begin
tmp_read_data = MTA1_VERSION;
end
if (address == ADDR_SWITCH_APP) begin
tmp_read_data = {32{switch_app_reg}};
end
if (address == ADDR_LED) begin
tmp_read_data = {29'h0, led_reg};
end
if (address == ADDR_GPIO) begin
tmp_read_data = {28'h0, gpio4_reg, gpio3_reg,
gpio2_reg[1], gpio1_reg[1]};
end
if (address == ADDR_APP_START) begin
tmp_read_data = app_start_reg;
end
if (address == ADDR_APP_SIZE) begin
tmp_read_data = app_size_reg;
end
if (address == ADDR_DEBUG) begin
tmp_read_data = debug_reg;
end
if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin
tmp_read_data = cdi_mem[address[2 : 0]];
end
if ((address >= ADDR_UDI_FIRST) && (address <= ADDR_UDI_LAST)) begin
tmp_read_data = udi_mem[address[0]];
end
end
end
end // api
endmodule // mta1
//======================================================================
// EOF mta1.v
//======================================================================

View File

@ -0,0 +1,15 @@
ISC License
Copyright (C) 2015 - 2021 Claire Xenia Wolf <claire@yosyshq.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,4 @@
# PicoRV32 - A Size-Optimized RISC-V CPU
This is a local copy of the
[PicoRV32](https://github.com/cliffordwolf/picorv32) RISC-V CPU core.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
# timer
A simple timer with prescaler written in Verilog.
## Introduction
This core implements a simple timer with a prescaler. The purpose of the prescaler is to more easily time durations rather than cycles. If for example setting the timer to the clock frequency, the timer can cound seconds.

View File

@ -0,0 +1,194 @@
//======================================================================
//
// timer.v
// --------
// Top level wrapper for the timer core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module timer(
input wire clk,
input wire reset_n,
input wire cs,
input wire we,
input wire [7 : 0] address,
input wire [31 : 0] write_data,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_CTRL = 8'h08;
localparam CTRL_START_BIT = 0;
localparam CTRL_STOP_BIT = 1;
localparam ADDR_STATUS = 8'h09;
localparam STATUS_READY_BIT = 0;
localparam ADDR_PRESCALER = 8'h0a;
localparam ADDR_TIMER = 8'h0b;
localparam CORE_NAME0 = 32'h74696d65; // "time"
localparam CORE_NAME1 = 32'h72202020; // "r "
localparam CORE_VERSION = 32'h00000003;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg [31 : 0] prescaler_reg;
reg prescaler_we;
reg [31 : 0] timer_reg;
reg timer_we;
reg start_reg;
reg start_new;
reg stop_reg;
reg stop_new;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
reg [31 : 0] tmp_read_data;
reg tmp_ready;
wire core_ready;
wire [31 : 0] core_curr_prescaler;
wire [31 : 0] core_curr_timer;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = tmp_ready;
//----------------------------------------------------------------
// core instantiation.
//----------------------------------------------------------------
timer_core core(
.clk(clk),
.reset_n(reset_n),
.prescaler_value(prescaler_reg),
.timer_value(timer_reg),
.start(start_reg),
.stop(stop_reg),
.curr_prescaler(core_curr_prescaler),
.curr_timer(core_curr_timer),
.ready(core_ready)
);
//----------------------------------------------------------------
// reg_update
//----------------------------------------------------------------
always @ (posedge clk)
begin : reg_update
if (!reset_n) begin
start_reg <= 1'h0;
stop_reg <= 1'h0;
prescaler_reg <= 32'h0;
timer_reg <= 32'h0;
end
else begin
start_reg <= start_new;
stop_reg <= stop_new;
if (prescaler_we) begin
prescaler_reg <= write_data;
end
if (timer_we) begin
timer_reg <= write_data;
end
end
end // reg_update
//----------------------------------------------------------------
// api
//
// The interface command decoding logic.
//----------------------------------------------------------------
always @*
begin : api
start_new = 1'h0;
stop_new = 1'h0;
prescaler_we = 1'h0;
timer_we = 1'h0;
tmp_read_data = 32'h0;
tmp_ready = 1'h0;
if (cs) begin
tmp_ready = 1'h1;
if (we) begin
if (address == ADDR_CTRL) begin
start_new = write_data[CTRL_START_BIT];
stop_new = write_data[CTRL_STOP_BIT];
end
if (core_ready) begin
if (address == ADDR_PRESCALER) begin
prescaler_we = 1'h1;
end
if (address == ADDR_TIMER) begin
timer_we = 1'h1;
end
end
end
else begin
if (address == ADDR_NAME0) begin
tmp_read_data = CORE_NAME0;
end
if (address == ADDR_NAME1) begin
tmp_read_data = CORE_NAME1;
end
if (address == ADDR_VERSION) begin
tmp_read_data = CORE_VERSION;
end
if (address == ADDR_STATUS) begin
tmp_read_data = {31'h0, core_ready};
end
if (address == ADDR_PRESCALER) begin
tmp_read_data = core_curr_prescaler;
end
if (address == ADDR_TIMER) begin
tmp_read_data = core_curr_timer;
end
end
end
end // addr_decoder
endmodule // timer
//======================================================================
// EOF timer.v
//======================================================================

View File

@ -0,0 +1,225 @@
//======================================================================
//
// timer_core.v
// ------------
// timer core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module timer_core(
input wire clk,
input wire reset_n,
input wire [31 : 0] prescaler_value,
input wire [31 : 0] timer_value,
input wire start,
input wire stop,
output wire [31 : 0] curr_prescaler,
output wire [31 : 0] curr_timer,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam CTRL_IDLE = 2'h0;
localparam CTRL_PRESCALER = 2'h1;
localparam CTRL_TIMER = 2'h2;
localparam CTRL_DONE = 2'h3;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg ready_reg;
reg ready_new;
reg ready_we;
reg [31 : 0] prescaler_reg;
reg [31 : 0] prescaler_new;
reg prescaler_we;
reg prescaler_set;
reg prescaler_dec;
reg [31 : 0] timer_reg;
reg [31 : 0] timer_new;
reg timer_we;
reg timer_set;
reg timer_dec;
reg [1 : 0] core_ctrl_reg;
reg [1 : 0] core_ctrl_new;
reg core_ctrl_we;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign curr_prescaler = prescaler_reg;
assign curr_timer = timer_reg;
assign ready = ready_reg;
//----------------------------------------------------------------
// reg_update
//----------------------------------------------------------------
always @ (posedge clk)
begin: reg_update
if (!reset_n)
begin
ready_reg <= 1'h1;
prescaler_reg <= 32'h0;
timer_reg <= 32'h0;
core_ctrl_reg <= CTRL_IDLE;
end
else
begin
if (ready_we) begin
ready_reg <= ready_new;
end
if (prescaler_we) begin
prescaler_reg <= prescaler_new;
end
if (timer_we) begin
timer_reg <= timer_new;
end
if (core_ctrl_we) begin
core_ctrl_reg <= core_ctrl_new;
end
end
end // reg_update
//----------------------------------------------------------------
// prescaler_ctr
//----------------------------------------------------------------
always @*
begin : prescaler_ctr
prescaler_new = 32'h0;
prescaler_we = 1'h0;
if (prescaler_set) begin
prescaler_new = prescaler_value;
prescaler_we = 1'h1;
end
else if (prescaler_dec) begin
prescaler_new = prescaler_reg - 1'h1;
prescaler_we = 1'h1;
end
end
//----------------------------------------------------------------
// timer_ctr
//----------------------------------------------------------------
always @*
begin : timer_ctr
timer_new = 32'h0;
timer_we = 1'h0;
if (timer_set) begin
timer_new = timer_value;
timer_we = 1'h1;
end
else if (timer_dec) begin
timer_new = timer_reg - 1'h1;
timer_we = 1'h1;
end
end
//----------------------------------------------------------------
// Core control FSM.
//----------------------------------------------------------------
always @*
begin : core_ctrl
ready_new = 1'h0;
ready_we = 1'h0;
prescaler_set = 1'h0;
prescaler_dec = 1'h0;
timer_set = 1'h0;
timer_dec = 1'h0;
core_ctrl_new = CTRL_IDLE;
core_ctrl_we = 1'h0;
case (core_ctrl_reg)
CTRL_IDLE: begin
if (start)
begin
ready_new = 1'h0;
ready_we = 1'h1;
prescaler_set = 1'h1;
timer_set = 1'h1;
core_ctrl_new = CTRL_PRESCALER;
core_ctrl_we = 1'h1;
end
end
CTRL_PRESCALER: begin
if (stop) begin
core_ctrl_new = CTRL_DONE;
core_ctrl_we = 1'h1;
end
else begin
if (prescaler_reg == 0) begin
core_ctrl_new = CTRL_TIMER;
core_ctrl_we = 1'h1;
end
else begin
prescaler_dec = 1'h1;
end
end
end
CTRL_TIMER: begin
if (stop) begin
core_ctrl_new = CTRL_DONE;
core_ctrl_we = 1'h1;
end
else begin
if (timer_reg == 0) begin
core_ctrl_new = CTRL_DONE;
core_ctrl_we = 1'h1;
end
else begin
prescaler_set = 1'h1;
timer_dec = 1'h1;
core_ctrl_new = CTRL_PRESCALER;
core_ctrl_we = 1'h1;
end
end
end
CTRL_DONE: begin
ready_new = 1'h1;
ready_we = 1'h1;
core_ctrl_new = CTRL_IDLE;
core_ctrl_we = 1'h1;
end
default: begin
end
endcase // case (core_ctrl_reg)
end // core_ctrl
endmodule // timer_core
//======================================================================
// EOF timer_core.v
//======================================================================

View File

@ -0,0 +1,293 @@
//======================================================================
//
// tb_timer.v
// -----------
// Testbench for the timer top level wrapper.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module tb_timer();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 0;
parameter DUMP_WAIT = 0;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_CTRL = 8'h08;
localparam CTRL_NEXT_BIT = 0;
localparam ADDR_STATUS = 8'h09;
localparam STATUS_READY_BIT = 0;
localparam ADDR_CONFIG = 8'h0a;
localparam CONFIG_ENCDEC_BIT = 0;
localparam ADDR_KEY0 = 8'h10;
localparam ADDR_KEY1 = 8'h11;
localparam ADDR_KEY2 = 8'h12;
localparam ADDR_KEY3 = 8'h13;
localparam ADDR_BLOCK0 = 8'h20;
localparam ADDR_BLOCK1 = 8'h21;
localparam ADDR_RESULT0 = 8'h30;
localparam ADDR_RESULT1 = 8'h31;
//----------------------------------------------------------------
// Register and Wire declarations.
//----------------------------------------------------------------
reg [31 : 0] cycle_ctr;
reg [31 : 0] error_ctr;
reg [31 : 0] tc_ctr;
reg tb_monitor;
reg tb_clk;
reg tb_reset_n;
reg tb_cs;
reg tb_we;
reg [7 : 0] tb_address;
reg [31 : 0] tb_write_data;
wire [31 : 0] tb_read_data;
reg [31 : 0] read_data;
//----------------------------------------------------------------
// Device Under Test.
//----------------------------------------------------------------
timer dut(
.clk(tb_clk),
.reset_n(tb_reset_n),
.cs(tb_cs),
.we(tb_we),
.address(tb_address),
.write_data(tb_write_data),
.read_data(tb_read_data)
);
//----------------------------------------------------------------
// clk_gen
//
// Always running clock generator process.
//----------------------------------------------------------------
always
begin : clk_gen
#CLK_HALF_PERIOD;
tb_clk = !tb_clk;
end // clk_gen
//----------------------------------------------------------------
// sys_monitor()
//
// An always running process that creates a cycle counter and
// conditionally displays information about the DUT.
//----------------------------------------------------------------
always
begin : sys_monitor
cycle_ctr = cycle_ctr + 1;
#(CLK_PERIOD);
if (tb_monitor)
begin
dump_dut_state();
end
end
//----------------------------------------------------------------
// dump_dut_state()
//
// Dump the state of the dump when needed.
//----------------------------------------------------------------
task dump_dut_state;
begin
$display("State of DUT");
$display("------------");
$display("Cycle: %08d", cycle_ctr);
$display("");
end
endtask // dump_dut_state
//----------------------------------------------------------------
// reset_dut()
//
// Toggle reset to put the DUT into a well known state.
//----------------------------------------------------------------
task reset_dut;
begin
$display("--- Toggle reset.");
tb_reset_n = 0;
#(2 * CLK_PERIOD);
tb_reset_n = 1;
end
endtask // reset_dut
//----------------------------------------------------------------
// display_test_result()
//
// Display the accumulated test results.
//----------------------------------------------------------------
task display_test_result;
begin
if (error_ctr == 0)
begin
$display("--- All %02d test cases completed successfully", tc_ctr);
end
else
begin
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
tc_ctr, error_ctr);
end
end
endtask // display_test_result
//----------------------------------------------------------------
// init_sim()
//
// Initialize all counters and testbed functionality as well
// as setting the DUT inputs to defined values.
//----------------------------------------------------------------
task init_sim;
begin
cycle_ctr = 0;
error_ctr = 0;
tc_ctr = 0;
tb_monitor = 0;
tb_clk = 1'h0;
tb_reset_n = 1'h1;
tb_cs = 1'h0;
tb_we = 1'h0;
tb_address = 8'h0;
tb_write_data = 32'h0;
end
endtask // init_sim
//----------------------------------------------------------------
// write_word()
//
// Write the given word to the DUT using the DUT interface.
//----------------------------------------------------------------
task write_word(input [11 : 0] address,
input [31 : 0] word);
begin
if (DEBUG)
begin
$display("--- Writing 0x%08x to 0x%02x.", word, address);
$display("");
end
tb_address = address;
tb_write_data = word;
tb_cs = 1;
tb_we = 1;
#(2 * CLK_PERIOD);
tb_cs = 0;
tb_we = 0;
end
endtask // write_word
//----------------------------------------------------------------
// read_word()
//
// Read a data word from the given address in the DUT.
// the word read will be available in the global variable
// read_data.
//----------------------------------------------------------------
task read_word(input [11 : 0] address);
begin
tb_address = address;
tb_cs = 1;
tb_we = 0;
#(CLK_PERIOD);
read_data = tb_read_data;
tb_cs = 0;
if (DEBUG)
begin
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
$display("");
end
end
endtask // read_word
//----------------------------------------------------------------
// wait_ready()
//
// Wait for the ready flag to be set in dut.
//----------------------------------------------------------------
task wait_ready;
begin : wready
read_word(ADDR_STATUS);
while (read_data == 0)
read_word(ADDR_STATUS);
end
endtask // wait_ready
//----------------------------------------------------------------
// test1()
//----------------------------------------------------------------
task test1;
begin
tc_ctr = tc_ctr + 1;
$display("");
$display("--- test1: started.");
$display("--- test1: completed.");
$display("");
end
endtask // tes1
//----------------------------------------------------------------
// timer_test
//----------------------------------------------------------------
initial
begin : timer_test
$display("");
$display(" -= Testbench for timer started =-");
$display(" =============================");
$display("");
init_sim();
reset_dut();
test1();
display_test_result();
$display("");
$display(" -= Testbench for timer started =-");
$display(" =============================");
$display("");
$finish;
end // timer_test
endmodule // tb_timer
//======================================================================
// EOF tb_timer.v
//======================================================================

View File

@ -0,0 +1,247 @@
//======================================================================
//
// tb_timer_core.v
// --------------
// Testbench for the timer core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module tb_timer_core();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 0;
parameter DUMP_WAIT = 0;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
//----------------------------------------------------------------
// Register and Wire declarations.
//----------------------------------------------------------------
reg [31 : 0] cycle_ctr;
reg [31 : 0] error_ctr;
reg [31 : 0] tc_ctr;
reg tb_monitor;
reg tb_clk;
reg tb_reset_n;
reg tb_start;
reg tb_stop;
reg [31 : 0] tb_prescaler;
reg [31 : 0] tb_timer;
wire [31 : 0] tb_curr_prescaler;
wire [31 : 0] tb_curr_timer;
wire tb_ready;
//----------------------------------------------------------------
// Device Under Test.
//----------------------------------------------------------------
timer_core dut(
.clk(tb_clk),
.reset_n(tb_reset_n),
.prescaler_value(tb_prescaler),
.timer_value(tb_timer),
.start(tb_start),
.stop(tb_stop),
.curr_prescaler(tb_curr_prescaler),
.curr_timer(tb_curr_timer),
.ready(tb_ready)
);
//----------------------------------------------------------------
// clk_gen
//
// Always running clock generator process.
//----------------------------------------------------------------
always
begin : clk_gen
#CLK_HALF_PERIOD;
tb_clk = !tb_clk;
end // clk_gen
//----------------------------------------------------------------
// sys_monitor()
//
// An always running process that creates a cycle counter and
// conditionally displays information about the DUT.
//----------------------------------------------------------------
always
begin : sys_monitor
cycle_ctr = cycle_ctr + 1;
#(CLK_PERIOD);
if (tb_monitor)
begin
dump_dut_state();
end
end
//----------------------------------------------------------------
// dump_dut_state()
//
// Dump the state of the dump when needed.
//----------------------------------------------------------------
task dump_dut_state;
begin
$display("State of DUT");
$display("------------");
$display("Cycle: %08d", cycle_ctr);
$display("");
$display("Inputs and outputs:");
$display("start: 0x%1x, stop: 0x%1x, ready: 0x%1x",
dut.start, dut.stop, dut.ready);
$display("prescaler_value: 0x%08x, timer_value: 0x%08x",
dut.prescaler_value, dut.timer_value);
$display("");
$display("Internal state:");
$display("prescaler_reg: 0x%08x, prescaler_new: 0x%08x",
dut.prescaler_reg, dut.prescaler_new);
$display("prescaler_set: 0x%1x, prescaler_dec: 0x%1x",
dut.prescaler_set, dut.prescaler_dec);
$display("");
$display("timer_reg: 0x%08x, timer_new: 0x%08x",
dut.timer_reg, dut.timer_new);
$display("timer_set: 0x%1x, timer_dec: 0x%1x",
dut.timer_set, dut.timer_dec);
$display("");
$display("core_ctrl_reg: 0x%02x, core_ctrl_new: 0x%02x, core_ctrl_we: 0x%1x",
dut.core_ctrl_reg, dut.core_ctrl_new, dut.core_ctrl_we);
$display("");
$display("");
end
endtask // dump_dut_state
//----------------------------------------------------------------
// reset_dut()
//
// Toggle reset to put the DUT into a well known state.
//----------------------------------------------------------------
task reset_dut;
begin
$display("--- DUT before reset:");
dump_dut_state();
$display("--- Toggling reset.");
tb_reset_n = 0;
#(2 * CLK_PERIOD);
tb_reset_n = 1;
$display("--- DUT after reset:");
dump_dut_state();
end
endtask // reset_dut
//----------------------------------------------------------------
// wait_ready()
//
// Wait for the ready flag in the dut to be set.
//
// Note: It is the callers responsibility to call the function
// when the dut is actively processing and will in fact at some
// point set the flag.
//----------------------------------------------------------------
task wait_ready;
begin
#(2 * CLK_PERIOD);
while (!tb_ready)
begin
#(CLK_PERIOD);
if (DUMP_WAIT)
begin
dump_dut_state();
end
end
end
endtask // wait_ready
//----------------------------------------------------------------
// init_sim()
//
// Initialize all counters and testbed functionality as well
// as setting the DUT inputs to defined values.
//----------------------------------------------------------------
task init_sim;
begin
cycle_ctr = 0;
error_ctr = 0;
tc_ctr = 0;
tb_monitor = 0;
tb_clk = 0;
tb_reset_n = 1;
tb_start = 1'h0;
tb_stop = 1'h0;
tb_prescaler = 32'h0;
tb_timer = 32'h0;
end
endtask // init_sim
//----------------------------------------------------------------
// test()
// Runs an encipher, decipher test with given key and plaintext
// The generated ciphertext is verified with the given ciphertet.
// The generated plaintxt is also verified against the
// given plaintext.
//----------------------------------------------------------------
task test1;
begin
tc_ctr = tc_ctr + 1;
tb_monitor = 1;
$display("--- test1 started.");
dump_dut_state();
tb_prescaler = 32'h6;
tb_timer = 32'h9;
#(CLK_PERIOD);
tb_start = 1'h1;
#(CLK_PERIOD);
tb_start = 1'h0;
wait_ready();
#(CLK_PERIOD);
tb_monitor = 0;
$display("--- test1 completed.");
$display("");
end
endtask // test1
//----------------------------------------------------------------
// timer_core_test
//
// Test vectors from:
//----------------------------------------------------------------
initial
begin : timer_core_test
$display("--- Simulation of TIMER core started.");
$display("");
init_sim();
reset_dut();
test1();
$display("");
$display("--- Simulation of timer core completed.");
$finish;
end // timer_core_test
endmodule // tb_timer_core
//======================================================================
// EOF tb_timer_core.v
//======================================================================

View File

@ -0,0 +1,75 @@
#===================================================================
#
# Makefile
# --------
# Makefile for building the timer core and top simulations.
#
#
# Author: Joachim Strombergson
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#===================================================================
CORE_SRC=../rtl/timer_core.v
TB_CORE_SRC =../tb/tb_timer_core.v
TOP_SRC=../rtl/timer.v $(CORE_SRC)
TB_TOP_SRC =../tb/tb_timer.v
CC = iverilog
CC_FLAGS = -Wall
LINT = verilator
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
all: top.sim core.sim
top.sim: $(TB_TOP_SRC) $(TOP_SRC)
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
core.sim: $(TB_CORE_SRC) $(CORE_SRC)
$(CC) $(CC_FLAGS) -o core.sim $(TB_CORE_SRC) $(CORE_SRC)
sim-top: top.sim
./top.sim
sim-core: core.sim
./core.sim
lint-core: $(CORE_SRC)
$(LINT) $(LINT_FLAGS) $(CORE_SRC)
lint-top: $(TOP_SRC)
$(LINT) $(LINT_FLAGS) $(TOP_SRC)
clean:
rm -f top.sim
rm -f core.sim
help:
@echo "Build system for simulation of Prince core"
@echo ""
@echo "Supported targets:"
@echo "------------------"
@echo "all: Build all simulation targets."
@echo "top.sim: Build top level simulation target."
@echo "core.sim: Build core level simulation target."
@echo "sim-top: Run top level simulation."
@echo "sim-core: Run core level simulation."
@echo "lint-core: Lint core rtl source files."
@echo "lint-core: Lint top rtl source files."
@echo "clean: Delete all built files."
#===================================================================
# EOF Makefile
#===================================================================

View File

@ -0,0 +1,5 @@
# touch_sense
Core that handles touch senor events and provides them via an API.
## Introduction
This core implements a touch sensor handler. The core detects and holds events for SW to read. The user must lift the finger between touch events.

View File

@ -0,0 +1,211 @@
//======================================================================
//
// touch_sense.v
// -------------
// Touch sensor handler.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module touch_sense(
input wire clk,
input wire reset_n,
input wire touch_event,
input wire cs,
input wire we,
input wire [7 : 0] address,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_STATUS = 8'h09;
localparam STATUS_EVENT_BIT = 0;
localparam CORE_NAME0 = 32'h745f7365; // "t_se"
localparam CORE_NAME1 = 32'h6e736520; // "nse "
localparam CORE_VERSION = 32'h00000001;
localparam CTRL_IDLE = 2'h0;
localparam CTRL_EVENT = 2'h1;
localparam CTRL_WAIT = 2'h2;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg touch_event_sample0_reg;
reg touch_event_sample1_reg;
reg touch_event_reg;
reg touch_event_new;
reg touch_event_set;
reg touch_event_rst;
reg touch_event_we;
reg [1 : 0] touch_sense_ctrl_reg;
reg [1 : 0] touch_sense_ctrl_new;
reg touch_sense_ctrl_we;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
reg [31 : 0] tmp_read_data;
reg [31 : 0] tmp_ready;
reg api_clear_event;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = tmp_ready;
//----------------------------------------------------------------
// reg_update
//----------------------------------------------------------------
always @ (posedge clk)
begin : reg_update
if (!reset_n) begin
touch_sense_ctrl_reg <= CTRL_IDLE;
touch_event_sample0_reg <= 1'h0;
touch_event_sample1_reg <= 1'h0;
touch_event_reg <= 1'h0;
end
else begin
touch_event_sample0_reg <= touch_event;
touch_event_sample1_reg <= touch_event_sample0_reg;
if (touch_event_we) begin
touch_event_reg <= touch_event_new;
end
if (touch_sense_ctrl_we) begin
touch_sense_ctrl_reg <= touch_sense_ctrl_new;
end
end
end // reg_update
//----------------------------------------------------------------
// api
//----------------------------------------------------------------
always @*
begin : api
api_clear_event = 1'h0;
tmp_read_data = 32'h0;
tmp_ready = 1'h0;
if (cs) begin
tmp_ready = 1'h1;
if (we) begin
if (address == ADDR_STATUS) begin
api_clear_event = 1'h1;
end
end
else begin
if (address == ADDR_NAME0) begin
tmp_read_data = CORE_NAME0;
end
if (address == ADDR_NAME1) begin
tmp_read_data = CORE_NAME1;
end
if (address == ADDR_VERSION) begin
tmp_read_data = CORE_VERSION;
end
if (address == ADDR_STATUS) begin
tmp_read_data[STATUS_EVENT_BIT] = touch_event_reg;
end
end
end
end // api
//----------------------------------------------------------------
// touch_event_reg_logic
//----------------------------------------------------------------
always @*
begin : touch_event_reg_logic
touch_event_new = 1'h0;
touch_event_we = 1'h0;
if (touch_event_set) begin
touch_event_new = 1'h1;
touch_event_we = 1'h1;
end
else if (touch_event_rst) begin
touch_event_new = 1'h0;
touch_event_we = 1'h1;
end
end
//----------------------------------------------------------------
// touch_sense_ctrl
//----------------------------------------------------------------
always @*
begin : touch_sense_ctrl
touch_event_set = 1'h0;
touch_event_rst = 1'h0;
touch_sense_ctrl_new = CTRL_IDLE;
touch_sense_ctrl_we = 1'h0;
case (touch_sense_ctrl_reg)
CTRL_IDLE : begin
if (touch_event_sample1_reg) begin
touch_event_set = 1'h1;
touch_sense_ctrl_new = CTRL_EVENT;
touch_sense_ctrl_we = 1'h1;
end
end
CTRL_EVENT: begin
if (api_clear_event) begin
touch_event_rst = 1'h1;
touch_sense_ctrl_new = CTRL_WAIT;
touch_sense_ctrl_we = 1'h1;
end
end
CTRL_WAIT: begin
if (!touch_event_sample1_reg) begin
touch_sense_ctrl_new = CTRL_IDLE;
touch_sense_ctrl_we = 1'h1;
end
end
default : begin
end
endcase // case (touch_sense_ctrl_reg)
end
endmodule // touch_sense
//======================================================================
// EOF touch_sense.v
//======================================================================

View File

@ -0,0 +1,276 @@
//======================================================================
//
// tb_touch_sense.v
// ----------------
// Testbench for the touch sense core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module tb_touch_sense();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 0;
parameter DUMP_WAIT = 0;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_STATUS = 8'h09;
localparam STATUS_READY_BIT = 0;
//----------------------------------------------------------------
// Register and Wire declarations.
//----------------------------------------------------------------
reg [31 : 0] cycle_ctr;
reg [31 : 0] error_ctr;
reg [31 : 0] tc_ctr;
reg tb_monitor;
reg tb_clk;
reg tb_reset_n;
reg tb_touch_event;
reg tb_cs;
reg tb_we;
reg [7 : 0] tb_address;
wire [31 : 0] tb_read_data;
reg [31 : 0] read_data;
//----------------------------------------------------------------
// Device Under Test.
//----------------------------------------------------------------
touch_sense dut(
.clk(tb_clk),
.reset_n(tb_reset_n),
.touch_event(tb_touch_event),
.cs(tb_cs),
.we(tb_we),
.address(tb_address),
.read_data(tb_read_data)
);
//----------------------------------------------------------------
// clk_gen
//
// Always running clock generator process.
//----------------------------------------------------------------
always
begin : clk_gen
#CLK_HALF_PERIOD;
tb_clk = !tb_clk;
end // clk_gen
//----------------------------------------------------------------
// sys_monitor()
//
// An always running process that creates a cycle counter and
// conditionally displays information about the DUT.
//----------------------------------------------------------------
always
begin : sys_monitor
cycle_ctr = cycle_ctr + 1;
#(CLK_PERIOD);
if (tb_monitor)
begin
dump_dut_state();
end
end
//----------------------------------------------------------------
// dump_dut_state()
//
// Dump the state of the dump when needed.
//----------------------------------------------------------------
task dump_dut_state;
begin
$display("State of DUT");
$display("------------");
$display("Cycle: %08d", cycle_ctr);
$display("");
end
endtask // dump_dut_state
//----------------------------------------------------------------
// reset_dut()
//
// Toggle reset to put the DUT into a well known state.
//----------------------------------------------------------------
task reset_dut;
begin
$display("--- Toggle reset.");
tb_reset_n = 0;
#(2 * CLK_PERIOD);
tb_reset_n = 1;
end
endtask // reset_dut
//----------------------------------------------------------------
// display_test_result()
//
// Display the accumulated test results.
//----------------------------------------------------------------
task display_test_result;
begin
if (error_ctr == 0)
begin
$display("--- All %02d test cases completed successfully", tc_ctr);
end
else
begin
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
tc_ctr, error_ctr);
end
end
endtask // display_test_result
//----------------------------------------------------------------
// init_sim()
//
// Initialize all counters and testbed functionality as well
// as setting the DUT inputs to defined values.
//----------------------------------------------------------------
task init_sim;
begin
cycle_ctr = 0;
error_ctr = 0;
tc_ctr = 0;
tb_monitor = 0;
tb_clk = 1'h0;
tb_reset_n = 1'h1;
tb_touch_event = 1'h0;
tb_cs = 1'h0;
tb_we = 1'h0;
tb_address = 8'h0;
end
endtask // init_sim
//----------------------------------------------------------------
// write_word()
//
// Write the given word to the DUT using the DUT interface.
//----------------------------------------------------------------
task write_word(input [11 : 0] address,
input [31 : 0] word);
begin
if (DEBUG)
begin
$display("--- Writing 0x%08x to 0x%02x.", word, address);
$display("");
end
tb_address = address;
tb_cs = 1;
tb_we = 1;
#(2 * CLK_PERIOD);
tb_cs = 0;
tb_we = 0;
end
endtask // write_word
//----------------------------------------------------------------
// read_word()
//
// Read a data word from the given address in the DUT.
// the word read will be available in the global variable
// read_data.
//----------------------------------------------------------------
task read_word(input [11 : 0] address);
begin
tb_address = address;
tb_cs = 1;
tb_we = 0;
#(CLK_PERIOD);
read_data = tb_read_data;
tb_cs = 0;
if (DEBUG)
begin
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
$display("");
end
end
endtask // read_word
//----------------------------------------------------------------
// wait_ready()
//
// Wait for the ready flag to be set in dut.
//----------------------------------------------------------------
task wait_ready;
begin : wready
read_word(ADDR_STATUS);
while (read_data == 0)
read_word(ADDR_STATUS);
end
endtask // wait_ready
//----------------------------------------------------------------
// test1()
//----------------------------------------------------------------
task test1;
begin
tc_ctr = tc_ctr + 1;
$display("");
$display("--- test1: started.");
$display("--- test1: completed.");
$display("");
end
endtask // tes1
//----------------------------------------------------------------
// touch_sense_test
//----------------------------------------------------------------
initial
begin : timer_test
$display("");
$display(" -= Testbench for touch_sense started =-");
$display(" ====================================");
$display("");
init_sim();
reset_dut();
test1();
display_test_result();
$display("");
$display(" -= Testbench for touch_sense completed =-");
$display(" ======================================");
$display("");
$finish;
end // touch_sense_test
endmodule // tb_touch_sense
//======================================================================
// EOF tb_touch_sense.v
//======================================================================

View File

@ -0,0 +1,56 @@
#===================================================================
#
# Makefile
# --------
# Makefile for building the touch sense top simulations.
#
#
# Author: Joachim Strombergson
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#===================================================================
TOP_SRC=../rtl/touch_sense.v
TB_TOP_SRC =../tb/tb_touch_sense.v
CC = iverilog
CC_FLAGS = -Wall
LINT = verilator
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
all: top.sim
top.sim: $(TB_TOP_SRC) $(TOP_SRC)
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
sim-top: top.sim
./top.sim
lint-top: $(TOP_SRC)
$(LINT) $(LINT_FLAGS) $(TOP_SRC)
clean:
rm -f top.sim
help:
@echo "Build system for simulation of touch sense core"
@echo ""
@echo "Supported targets:"
@echo "------------------"
@echo "all: Build all simulation targets."
@echo "top.sim: Build top level simulation target."
@echo "sim-top: Run top level simulation."
@echo "lint-top: Lint top rtl source files."
@echo "clean: Delete all built files."
#===================================================================
# EOF Makefile
#===================================================================

View File

@ -0,0 +1,29 @@
# trng
Implementation of the FiGaRO TRNG for FPGAs
## Introduction
# figaro
## Status
First version completed. In testing. Use with caution.
## Introduction
This is a an implementation of the FiGaRO true random
number generator (TRNG) [1]. The main FPGA target is Lattice iCE40
UltraPlus, but adaption to other FPGAs should be easy to do.
## Implementation details
The implementation instantiates four FiRO and four GaRO modules. The
modules includes state sampling. The polynomials used for the
oscillators are given by equotions (9)..(16) in paper [1]. The eight
outputs are then XORed together to form a one bit random value.
The random bit value is sampled at a rate controlled by a 24 bit
divisor.
## References
[1] [True Random Number Generator Based on Fibonacci-Galois
Ring Oscillators for FPGA](https://www.mdpi.com/2076-3417/11/8/3330/pdf)

View File

@ -0,0 +1,129 @@
//======================================================================
//
// figaro.v
// --------
// Top level wrapper for the figaro core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module figaro(
input wire clk,
input wire reset_n,
input wire cs,
input wire we,
input wire [7 : 0] address,
input wire [31 : 0] write_data,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_STATUS = 8'h09;
localparam STATUS_READY_BIT = 0;
localparam ADDR_SAMPLE_RATE = 8'h10;
localparam ADDR_ENTROPY = 8'h20;
localparam CORE_NAME0 = 32'h66696761; // "figa"
localparam CORE_NAME1 = 32'h726f2020; // "ro "
localparam CORE_VERSION = 32'h00000001;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
reg core_read_entropy;
reg core_set_sample_rate;
wire [31 : 0] core_entropy;
wire core_ready;
reg [31 : 0] tmp_read_data;
reg tmp_ready;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = tmp_ready;
//----------------------------------------------------------------
// core instantiation.
//----------------------------------------------------------------
figaro_core core(
.clk(clk),
.reset_n(reset_n),
.read_entropy(core_read_entropy),
.set_sample_rate(core_set_sample_rate),
.sample_rate(write_data[23 : 0]),
.entropy(core_entropy),
.ready(core_ready)
);
//----------------------------------------------------------------
// api
//
// The interface command decoding logic.
//----------------------------------------------------------------
always @*
begin : api
core_read_entropy = 1'h0;
core_set_sample_rate = 1'h0;
tmp_read_data = 32'h0;
tmp_ready = 1'h0;
if (cs) begin
tmp_ready = 1'h1;
if (we) begin
if (address == ADDR_SAMPLE_RATE) begin
core_set_sample_rate = 1'h1;
end
end
else begin
if (address == ADDR_NAME0) begin
tmp_read_data = CORE_NAME0;
end
if (address == ADDR_NAME1) begin
tmp_read_data = CORE_NAME1;
end
if (address == ADDR_VERSION) begin
tmp_read_data = CORE_VERSION;
end
if (address == ADDR_STATUS) begin
tmp_read_data = {31'h0, core_ready};
end
if (address == ADDR_ENTROPY) begin
tmp_read_data = core_entropy;
core_read_entropy = 1'h1;
end
end
end
end // api
endmodule // figaro
//======================================================================
// EOF figaro.v
//======================================================================

View File

@ -0,0 +1,204 @@
//======================================================================
//
// figaro_core.v
// -----------
// FiGaRO based FIGARO for iCE40 device.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
module figaro_core(
input wire clk,
input wire reset_n,
input wire set_sample_rate,
input wire [23 : 0] sample_rate,
input wire read_entropy,
output wire [31 : 0] entropy,
output wire ready
);
//---------------------------------------------------------------
// Local parameters.
//---------------------------------------------------------------
localparam DEFAULT_SAMPLE_RATE = 24'h010000;
//---------------------------------------------------------------
// Registers.
//---------------------------------------------------------------
reg [23 : 0] sample_rate_ctr_reg;
reg [23 : 0] sample_rate_ctr_new;
reg [23 : 0] sample_rate_reg;
reg sample_rate_we;
reg [5 : 0] bit_ctr_reg;
reg [5 : 0] bit_ctr_new;
reg bit_ctr_rst;
reg bit_ctr_inc;
reg bit_ctr_we;
reg [31 : 0] entropy_reg;
reg [31 : 0] entropy_new;
reg entropy_we;
reg ready_reg;
reg ready_new;
reg ready_we;
//---------------------------------------------------------------
// Firo oscillator instances and XOR combined result.
//---------------------------------------------------------------
wire firo_ent[3 : 0];
wire firo_entropy;
firo #(.POLY(10'b1111110111)) firo0(.clk(clk), .entropy(firo_ent[0]));
firo #(.POLY(10'b1011111001)) firo1(.clk(clk), .entropy(firo_ent[1]));
firo #(.POLY(10'b1100000001)) firo2(.clk(clk), .entropy(firo_ent[2]));
firo #(.POLY(10'b1011111111)) firo3(.clk(clk), .entropy(firo_ent[3]));
assign firo_entropy = firo_ent[0] ^ firo_ent[1] ^
firo_ent[2] ^ firo_ent[3];
//---------------------------------------------------------------
// garo oscillator instances and XOR combined result.
//---------------------------------------------------------------
wire garo_ent[3 : 0];
wire garo_entropy;
garo #(.POLY(11'b11111101111)) garo0(.clk(clk), .entropy(garo_ent[0]));
garo #(.POLY(11'b10111110011)) garo1(.clk(clk), .entropy(garo_ent[1]));
garo #(.POLY(11'b11000000011)) garo2(.clk(clk), .entropy(garo_ent[2]));
garo #(.POLY(11'b10111111111)) garo3(.clk(clk), .entropy(garo_ent[3]));
assign garo_entropy = garo_ent[0] ^ garo_ent[1] ^
garo_ent[2] ^ garo_ent[3];
//---------------------------------------------------------------
// Assignments.
//---------------------------------------------------------------
assign ready = ready_reg;
assign entropy = entropy_reg;
//---------------------------------------------------------------
// reg_update
//---------------------------------------------------------------
always @(posedge clk)
begin : reg_update
if (!reset_n) begin
sample_rate_reg <= DEFAULT_SAMPLE_RATE;
sample_rate_ctr_reg <= 24'h0;
bit_ctr_reg <= 6'h0;
entropy_reg <= 32'h0;
ready_reg <= 1'h0;
end
else begin
sample_rate_ctr_reg <= sample_rate_ctr_new;
if (sample_rate_we) begin
sample_rate_reg <= sample_rate;
end
if (bit_ctr_we) begin
bit_ctr_reg <= bit_ctr_new;
end
if (entropy_we) begin
entropy_reg <= entropy_new;
end
if (ready_we) begin
ready_reg <= ready_new;
end
end
end
//---------------------------------------------------------------
// ready_logic
//
// After an entropy word has been read we wait 32 bits before
// setting ready again, indicating that a new word is ready.
//---------------------------------------------------------------
always @*
begin : ready_logic;
bit_ctr_new = 6'h0;
bit_ctr_we = 1'h0;
ready_new = 1'h0;
ready_we = 1'h0;
if (bit_ctr_reg >= 6'h20) begin
ready_new = 1'h1;
ready_we = 1'h1;
end
if (bit_ctr_rst) begin
bit_ctr_new = 6'h0;
bit_ctr_we = 1'h1;
ready_new = 1'h0;
ready_we = 1'h1;
end
else if (bit_ctr_inc) begin
if (bit_ctr_reg < 6'h24) begin
bit_ctr_new = bit_ctr_reg + 1'h1;
bit_ctr_we = 1'h1;
end
end
end
//---------------------------------------------------------------
// figaro_sample_logic
//
// Wait sample_rate_reg number of cycles between sampling a bit
// from the entropy source.
//---------------------------------------------------------------
always @*
begin : figaro_sample_logic
sample_rate_we = 1'h0;
bit_ctr_rst = 1'h0;
bit_ctr_inc = 1'h0;
entropy_we = 1'h0;
entropy_new = {entropy_reg[30 : 0], firo_entropy ^ garo_entropy};
if (read_entropy) begin
bit_ctr_rst = 1'h1;
sample_rate_ctr_new = 24'h0;
end
else if (set_sample_rate) begin
bit_ctr_rst = 1'h1;
sample_rate_we = 1'h1;
sample_rate_ctr_new = 24'h0;
end
else if (sample_rate_ctr_reg == sample_rate_reg) begin
sample_rate_ctr_new = 24'h0;
entropy_we = 1'h1;
bit_ctr_inc = 1'h1;
end
else begin
sample_rate_ctr_new = sample_rate_ctr_reg + 1'h1;
end
end
endmodule // figaro_core
//======================================================================
// EOF figaro_core.v
//======================================================================

View File

@ -0,0 +1,75 @@
//======================================================================
//
// firo.v
// ------
// Fibonacci Ring Oscillator with state sampling.
// The Fibonacci depth is 10 bits, and the bits are always sampled.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module firo(
input wire clk,
output wire entropy
);
parameter POLY = 10'b1111111111;
//----------------------------------------------------------------
// Registers and wires.
//----------------------------------------------------------------
reg entropy_reg;
wire [10 : 0] f;
//---------------------------------------------------------------
// Combinational loop inverters.
//---------------------------------------------------------------
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv1 (.I0(f[0]), .O(f[1]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv2 (.I0(f[1]), .O(f[2]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv3 (.I0(f[2]), .O(f[3]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv4 (.I0(f[3]), .O(f[4]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv5 (.I0(f[4]), .O(f[5]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv6 (.I0(f[5]), .O(f[6]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv7 (.I0(f[6]), .O(f[7]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv8 (.I0(f[7]), .O(f[8]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv9 (.I0(f[8]), .O(f[9]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv10 (.I0(f[9]), .O(f[10]));
//---------------------------------------------------------------
// parameterized feedback logic.
//---------------------------------------------------------------
assign f[0] = (POLY[0] & f[1]) ^ (POLY[1] & f[2]) ^
(POLY[2] & f[3]) ^ (POLY[3] & f[4]) ^
(POLY[4] & f[5]) ^ (POLY[5] & f[6]) ^
(POLY[6] & f[7]) ^ (POLY[7] & f[8]) ^
(POLY[8] & f[9]) ^ (POLY[9] & f[10]);
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign entropy = entropy_reg;
//---------------------------------------------------------------
// reg_update
//---------------------------------------------------------------
always @(posedge clk)
begin : reg_update
entropy_reg <= ^f;
end
endmodule // firo
//======================================================================
// EOF firo.v
//======================================================================

View File

@ -0,0 +1,85 @@
//======================================================================
//
// garo.v
// ------
// GaloisRing Oscillator with state sampling.
// The Galois depth is 11 bits, and the bits are always sampled.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module garo(
input wire clk,
output wire entropy
);
parameter POLY = 11'b11111111111;
//----------------------------------------------------------------
// Registers and wires.
//----------------------------------------------------------------
reg entropy_reg;
wire [11 : 0] g;
wire [11 : 0] gp;
//---------------------------------------------------------------
// Combinational loop inverters.
//---------------------------------------------------------------
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv1 (.I0(g[0]), .O(gp[0]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv2 (.I0(g[1]), .O(gp[1]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv3 (.I0(g[2]), .O(gp[2]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv4 (.I0(g[3]), .O(gp[3]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv5 (.I0(g[4]), .O(gp[4]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv6 (.I0(g[5]), .O(gp[5]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv7 (.I0(g[6]), .O(gp[6]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv8 (.I0(g[7]), .O(gp[7]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv9 (.I0(g[8]), .O(gp[8]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv10 (.I0(g[9]), .O(gp[9]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv11 (.I0(g[10]), .O(gp[10]));
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv12 (.I0(g[11]), .O(gp[11]));
//---------------------------------------------------------------
// parameterized feedback logic.
//---------------------------------------------------------------
assign g[11] = gp[0];
assign g[10] = gp[11] ^ (POLY[10] & gp[0]);
assign g[9] = gp[10] ^ (POLY[9] & gp[0]);
assign g[8] = gp[9] ^ (POLY[8] & gp[0]);
assign g[7] = gp[8] ^ (POLY[7] & gp[0]);
assign g[6] = gp[7] ^ (POLY[6] & gp[0]);
assign g[5] = gp[6] ^ (POLY[5] & gp[0]);
assign g[4] = gp[5] ^ (POLY[4] & gp[0]);
assign g[3] = gp[4] ^ (POLY[3] & gp[0]);
assign g[2] = gp[3] ^ (POLY[2] & gp[0]);
assign g[1] = gp[2] ^ (POLY[1] & gp[0]);
assign g[0] = gp[1] ^ (POLY[0] & gp[0]);
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign entropy = entropy_reg;
//---------------------------------------------------------------
// reg_update
//---------------------------------------------------------------
always @(posedge clk)
begin : reg_update
entropy_reg <= ^g;
end
endmodule // garo
//======================================================================
// EOF garo.v
//======================================================================

View File

@ -0,0 +1,23 @@
Copyright (c) 2014, Joachim Strömbergson
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,9 @@
uart
====
A simple universal asynchronous receiver/transmitter (UART) core
implemented in Verilog.
# Status
The core is completed and has been used in several FPGA designs.

View File

@ -0,0 +1,306 @@
//======================================================================
//
// uart.v
// ------
// Top level wrapper for the uart core.
//
// A simple universal asynchronous receiver/transmitter (UART)
// interface. The interface contains 16 byte wide transmit and
// receivea buffers and can handle start and stop bits. But in
// general is rather simple. The primary purpose is as host
// interface for the coretest design. The core also has a
// loopback mode to allow testing of a serial link.
//
// Note that the UART has a separate API interface to allow
// a control core to change settings such as speed. But the core
// has default values to allow it to start operating directly
// after reset. No config should be needed.
//
//
// Author: Joachim Strombergson
// Copyright (c) 2014, Secworks Sweden AB
// SPDX-License-Identifier: BSD-2-Clause
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================
module uart(
input wire clk,
input wire reset_n,
input wire rxd,
output wire txd,
input wire cs,
input wire we,
input wire [7 : 0] address,
input wire [31 : 0] write_data,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam ADDR_CORE_NAME0 = 8'h00;
localparam ADDR_CORE_NAME1 = 8'h01;
localparam ADDR_CORE_VERSION = 8'h02;
localparam ADDR_BIT_RATE = 8'h10;
localparam ADDR_DATA_BITS = 8'h11;
localparam ADDR_STOP_BITS = 8'h12;
localparam ADDR_RX_STATUS = 8'h20;
localparam ADDR_RX_DATA = 8'h21;
localparam ADDR_TX_STATUS = 8'h40;
localparam ADDR_TX_DATA = 8'h41;
localparam CORE_NAME0 = 32'h75617274; // "uart"
localparam CORE_NAME1 = 32'h20202020; // " "
localparam CORE_VERSION = 32'h00000004;
// The default bit rate is based on target clock frequency
// divided by the bit rate times in order to hit the
// center of the bits. I.e.
// Clock: 12 MHz, 38400 bps
// Divisor = 12*10E6 / 38400 = 312
localparam DEFAULT_BIT_RATE = 16'd312;
localparam DEFAULT_DATA_BITS = 4'h8;
localparam DEFAULT_STOP_BITS = 2'h1;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg [15 : 0] bit_rate_reg;
reg bit_rate_we;
reg [3 : 0] data_bits_reg;
reg data_bits_we;
reg [1 : 0] stop_bits_reg;
reg stop_bits_we;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
wire core_rxd_syn;
wire [7 : 0] core_rxd_data;
wire core_rxd_ack;
reg core_txd_syn;
reg [7 : 0] core_txd_data;
wire core_txd_ready;
wire fifo_out_syn;
wire [7 : 0] fifo_out_data;
reg fifo_out_ack;
reg [31 : 0] tmp_read_data;
reg tmp_ready;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = tmp_ready;
//----------------------------------------------------------------
// Module instantiations.
//----------------------------------------------------------------
uart_core core(
.clk(clk),
.reset_n(reset_n),
// Configuration parameters
.bit_rate(bit_rate_reg),
.data_bits(data_bits_reg),
.stop_bits(stop_bits_reg),
// External data interface
.rxd(rxd),
.txd(txd),
// Internal receive interface.
.rxd_syn(core_rxd_syn),
.rxd_data(core_rxd_data),
.rxd_ack(core_rxd_ack),
// Internal transmit interface.
.txd_syn(core_txd_syn),
.txd_data(core_txd_data),
.txd_ready(core_txd_ready)
);
uart_fifo fifo(
.clk(clk),
.reset_n(reset_n),
.in_syn(core_rxd_syn),
.in_data(core_rxd_data),
.in_ack(core_rxd_ack),
.out_syn(fifo_out_syn),
.out_data(fifo_out_data),
.out_ack(fifo_out_ack)
);
//----------------------------------------------------------------
// reg_update
//
// Update functionality for all registers in the core.
// All registers are positive edge triggered with synchronous
// active low reset.
//----------------------------------------------------------------
always @ (posedge clk)
begin: reg_update
if (!reset_n) begin
bit_rate_reg <= DEFAULT_BIT_RATE;
data_bits_reg <= DEFAULT_DATA_BITS;
stop_bits_reg <= DEFAULT_STOP_BITS;
end
else begin
if (bit_rate_we) begin
bit_rate_reg <= write_data[15 : 0];
end
if (data_bits_we) begin
data_bits_reg <= write_data[3 : 0];
end
if (stop_bits_we) begin
stop_bits_reg <= write_data[1 : 0];
end
end
end // reg_update
//----------------------------------------------------------------
// api
//
// The core API that allows an internal host to control the
// core functionality.
//----------------------------------------------------------------
always @*
begin: api
// Default assignments.
bit_rate_we = 1'h0;
data_bits_we = 1'h0;
stop_bits_we = 1'h0;
core_txd_syn = 1'h0;
fifo_out_ack = 1'h0;
tmp_read_data = 32'h0;
tmp_ready = 1'h0;
core_txd_data = write_data[7 : 0];
if (cs) begin
tmp_ready = 1'h1;
if (we) begin
case (address)
ADDR_BIT_RATE: begin
bit_rate_we = 1;
end
ADDR_DATA_BITS: begin
data_bits_we = 1;
end
ADDR_STOP_BITS: begin
stop_bits_we = 1;
end
ADDR_TX_DATA: begin
core_txd_syn = 1'h1;
end
default: begin
end
endcase // case (address)
end
else begin
case (address)
ADDR_CORE_NAME0: begin
tmp_read_data = CORE_NAME0;
end
ADDR_CORE_NAME1: begin
tmp_read_data = CORE_NAME1;
end
ADDR_CORE_VERSION: begin
tmp_read_data = CORE_VERSION;
end
ADDR_BIT_RATE: begin
tmp_read_data = {16'h0, bit_rate_reg};
end
ADDR_DATA_BITS: begin
tmp_read_data = {28'h0, data_bits_reg};
end
ADDR_STOP_BITS: begin
tmp_read_data = {30'h0, stop_bits_reg};
end
ADDR_RX_STATUS: begin
tmp_read_data = {31'h0, fifo_out_syn};
end
ADDR_RX_DATA: begin
fifo_out_ack = 1'h1;
tmp_read_data = {24'h0, fifo_out_data};
end
ADDR_TX_STATUS: begin
tmp_read_data = {31'h0, core_txd_ready};
end
default: begin
end
endcase // case (address)
end
end
end
endmodule // uart
//======================================================================
// EOF uart.v
//======================================================================

View File

@ -0,0 +1,532 @@
//======================================================================
//
// uart_core.v
// -----------
// A simple universal asynchronous receiver/transmitter (UART)
// interface. The interface contains 16 byte wide transmit and
// receivea buffers and can handle start and stop bits. But in
// general is rather simple. The primary purpose is as host
// interface for the coretest design. The core also has a
// loopback mode to allow testing of a serial link.
//
// Note that the UART has a separate API interface to allow
// a control core to change settings such as speed. But the core
// has default values to allow it to start operating directly
// after reset. No config should be needed.
//
//
// Author: Joachim Strombergson
// Copyright (c) 2014, Secworks Sweden AB
//
// SPDX-License-Identifier: BSD-2-Clause
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================
module uart_core(
input wire clk,
input wire reset_n,
// Configuration parameters
input wire [15 : 0] bit_rate,
input wire [3 : 0] data_bits,
input wire [1 : 0] stop_bits,
// External data interface
input wire rxd,
output wire txd,
// Internal receive interface.
output wire rxd_syn,
output [7 : 0] rxd_data,
input wire rxd_ack,
// Internal transmit interface.
input wire txd_syn,
input wire [7 : 0] txd_data,
output wire txd_ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter ERX_IDLE = 0;
parameter ERX_START = 1;
parameter ERX_BITS = 2;
parameter ERX_STOP = 3;
parameter ERX_SYN = 4;
parameter ETX_IDLE = 0;
parameter ETX_ACK = 1;
parameter ETX_START = 2;
parameter ETX_BITS = 3;
parameter ETX_STOP = 4;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg rxd0_reg;
reg rxd_reg;
reg [7 : 0] rxd_byte_reg;
reg rxd_byte_we;
reg [4 : 0] rxd_bit_ctr_reg;
reg [4 : 0] rxd_bit_ctr_new;
reg rxd_bit_ctr_we;
reg rxd_bit_ctr_rst;
reg rxd_bit_ctr_inc;
reg [15 : 0] rxd_bitrate_ctr_reg;
reg [15 : 0] rxd_bitrate_ctr_new;
reg rxd_bitrate_ctr_we;
reg rxd_bitrate_ctr_rst;
reg rxd_bitrate_ctr_inc;
reg rxd_syn_reg;
reg rxd_syn_new;
reg rxd_syn_we;
reg [2 : 0] erx_ctrl_reg;
reg [2 : 0] erx_ctrl_new;
reg erx_ctrl_we;
reg txd_reg;
reg txd_new;
reg txd_we;
reg [7 : 0] txd_byte_reg;
reg [7 : 0] txd_byte_new;
reg txd_byte_we;
reg [4 : 0] txd_bit_ctr_reg;
reg [4 : 0] txd_bit_ctr_new;
reg txd_bit_ctr_we;
reg txd_bit_ctr_rst;
reg txd_bit_ctr_inc;
reg [15 : 0] txd_bitrate_ctr_reg;
reg [15 : 0] txd_bitrate_ctr_new;
reg txd_bitrate_ctr_we;
reg txd_bitrate_ctr_rst;
reg txd_bitrate_ctr_inc;
reg txd_ready_reg;
reg txd_ready_new;
reg txd_ready_we;
reg [2 : 0] etx_ctrl_reg;
reg [2 : 0] etx_ctrl_new;
reg etx_ctrl_we;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
wire [15 : 0] half_bit_rate;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign txd = txd_reg;
assign rxd_syn = rxd_syn_reg;
assign rxd_data = rxd_byte_reg;
assign txd_ready = txd_ready_reg;
assign half_bit_rate = {1'b0, bit_rate[15 : 1]};
//----------------------------------------------------------------
// reg_update
//
// Update functionality for all registers in the core.
// All registers are positive edge triggered with
// synchronous active low reset.
//----------------------------------------------------------------
always @ (posedge clk)
begin: reg_update
if (!reset_n) begin
rxd0_reg <= 1'b0;
rxd_reg <= 1'b0;
rxd_byte_reg <= 8'h0;
rxd_bit_ctr_reg <= 5'h0;
rxd_bitrate_ctr_reg <= 16'h0;
rxd_syn_reg <= 0;
erx_ctrl_reg <= ERX_IDLE;
txd_reg <= 1'b1;
txd_byte_reg <= 8'h0;
txd_bit_ctr_reg <= 5'h0;
txd_bitrate_ctr_reg <= 16'h0;
txd_ready_reg <= 1'b1;
etx_ctrl_reg <= ETX_IDLE;
end
else begin
rxd0_reg <= rxd;
rxd_reg <= rxd0_reg;
if (rxd_byte_we) begin
rxd_byte_reg <= {rxd_reg, rxd_byte_reg[7 : 1]};
end
if (rxd_bit_ctr_we) begin
rxd_bit_ctr_reg <= rxd_bit_ctr_new;
end
if (rxd_bitrate_ctr_we) begin
rxd_bitrate_ctr_reg <= rxd_bitrate_ctr_new;
end
if (rxd_syn_we) begin
rxd_syn_reg <= rxd_syn_new;
end
if (erx_ctrl_we) begin
erx_ctrl_reg <= erx_ctrl_new;
end
if (txd_we) begin
txd_reg <= txd_new;
end
if (txd_byte_we) begin
txd_byte_reg <= txd_byte_new;
end
if (txd_bit_ctr_we) begin
txd_bit_ctr_reg <= txd_bit_ctr_new;
end
if (txd_bitrate_ctr_we) begin
txd_bitrate_ctr_reg <= txd_bitrate_ctr_new;
end
if (txd_ready_we) begin
txd_ready_reg <= txd_ready_new;
end
if (etx_ctrl_we) begin
etx_ctrl_reg <= etx_ctrl_new;
end
end
end // reg_update
//----------------------------------------------------------------
// rxd_bit_ctr
//
// Bit counter for receiving data on the external
// serial interface.
//----------------------------------------------------------------
always @*
begin: rxd_bit_ctr
rxd_bit_ctr_new = 5'h0;
rxd_bit_ctr_we = 1'b0;
if (rxd_bit_ctr_rst) begin
rxd_bit_ctr_new = 5'h0;
rxd_bit_ctr_we = 1'b1;
end
else if (rxd_bit_ctr_inc) begin
rxd_bit_ctr_new = rxd_bit_ctr_reg + 1'h1;
rxd_bit_ctr_we = 1'b1;
end
end // rxd_bit_ctr
//----------------------------------------------------------------
// rxd_bitrate_ctr
//
// Bitrate counter for receiving data on the external
// serial interface.
//----------------------------------------------------------------
always @*
begin: rxd_bitrate_ctr
rxd_bitrate_ctr_new = 16'h0;
rxd_bitrate_ctr_we = 1'h0;
if (rxd_bitrate_ctr_rst) begin
rxd_bitrate_ctr_new = 16'h0;
rxd_bitrate_ctr_we = 1'b1;
end
else if (rxd_bitrate_ctr_inc) begin
rxd_bitrate_ctr_new = rxd_bitrate_ctr_reg + 1'h1;
rxd_bitrate_ctr_we = 1'b1;
end
end // rxd_bitrate_ctr
//----------------------------------------------------------------
// txd_bit_ctr
//
// Bit counter for transmitting data on the external
// serial interface.
//----------------------------------------------------------------
always @*
begin: txd_bit_ctr
txd_bit_ctr_new = 5'h0;
txd_bit_ctr_we = 1'h0;
if (txd_bit_ctr_rst) begin
txd_bit_ctr_new = 5'h0;
txd_bit_ctr_we = 1'h1;
end
else if (txd_bit_ctr_inc) begin
txd_bit_ctr_new = txd_bit_ctr_reg + 1'h1;
txd_bit_ctr_we = 1'b1;
end
end // txd_bit_ctr
//----------------------------------------------------------------
// txd_bitrate_ctr
//
// Bitrate counter for transmitting data on the external
// serial interface.
//----------------------------------------------------------------
always @*
begin: txd_bitrate_ctr
txd_bitrate_ctr_new = 16'h0;
txd_bitrate_ctr_we = 0;
if (txd_bitrate_ctr_rst) begin
txd_bitrate_ctr_new = 16'h0;
txd_bitrate_ctr_we = 1;
end
else if (txd_bitrate_ctr_inc) begin
txd_bitrate_ctr_new = txd_bitrate_ctr_reg + 1'h1;
txd_bitrate_ctr_we = 1;
end
end // txd_bitrate_ctr
//----------------------------------------------------------------
// external_rx_engine
//
// Logic that implements the receive engine towards
// the external interface. Detects incoming data, collects it,
// if required checks parity and store correct data into
// the rx buffer.
//----------------------------------------------------------------
always @*
begin: external_rx_engine
rxd_bit_ctr_rst = 0;
rxd_bit_ctr_inc = 0;
rxd_bitrate_ctr_rst = 0;
rxd_bitrate_ctr_inc = 0;
rxd_byte_we = 0;
rxd_syn_new = 0;
rxd_syn_we = 0;
erx_ctrl_new = ERX_IDLE;
erx_ctrl_we = 0;
case (erx_ctrl_reg)
ERX_IDLE: begin
if (!rxd_reg) begin
// Possible start bit detected.
rxd_bitrate_ctr_rst = 1;
erx_ctrl_new = ERX_START;
erx_ctrl_we = 1;
end
end
ERX_START: begin
rxd_bitrate_ctr_inc = 1;
if (rxd_reg) begin
// Just a glitch.
erx_ctrl_new = ERX_IDLE;
erx_ctrl_we = 1;
end
else begin
if (rxd_bitrate_ctr_reg == half_bit_rate) begin
// start bit assumed. We start sampling data.
rxd_bit_ctr_rst = 1;
rxd_bitrate_ctr_rst = 1;
erx_ctrl_new = ERX_BITS;
erx_ctrl_we = 1;
end
end
end
ERX_BITS: begin
if (rxd_bitrate_ctr_reg < bit_rate) begin
rxd_bitrate_ctr_inc = 1;
end
else begin
rxd_byte_we = 1;
rxd_bit_ctr_inc = 1;
rxd_bitrate_ctr_rst = 1;
if (rxd_bit_ctr_reg == data_bits - 1) begin
erx_ctrl_new = ERX_STOP;
erx_ctrl_we = 1;
end
end
end
ERX_STOP: begin
rxd_bitrate_ctr_inc = 1;
if (rxd_bitrate_ctr_reg == bit_rate * stop_bits) begin
rxd_syn_new = 1;
rxd_syn_we = 1;
erx_ctrl_new = ERX_SYN;
erx_ctrl_we = 1;
end
end
ERX_SYN: begin
if (rxd_ack) begin
rxd_syn_new = 0;
rxd_syn_we = 1;
erx_ctrl_new = ERX_IDLE;
erx_ctrl_we = 1;
end
end
default: begin
end
endcase // case (erx_ctrl_reg)
end // external_rx_engine
//----------------------------------------------------------------
// external_tx_engine
//
// Logic that implements the transmit engine towards
// the external interface.
//----------------------------------------------------------------
always @*
begin: external_tx_engine
txd_new = 0;
txd_we = 0;
txd_byte_new = 0;
txd_byte_we = 0;
txd_bit_ctr_rst = 0;
txd_bit_ctr_inc = 0;
txd_bitrate_ctr_rst = 0;
txd_bitrate_ctr_inc = 0;
txd_ready_new = 0;
txd_ready_we = 0;
etx_ctrl_new = ETX_IDLE;
etx_ctrl_we = 0;
case (etx_ctrl_reg)
ETX_IDLE: begin
txd_new = 1;
txd_we = 1;
if (txd_syn) begin
txd_byte_new = txd_data;
txd_byte_we = 1;
txd_ready_new = 0;
txd_ready_we = 1;
txd_bitrate_ctr_rst = 1;
etx_ctrl_new = ETX_ACK;
etx_ctrl_we = 1;
end
end
ETX_ACK: begin
if (!txd_syn) begin
txd_new = 0;
txd_we = 1;
etx_ctrl_new = ETX_START;
etx_ctrl_we = 1;
end
end
ETX_START: begin
if (txd_bitrate_ctr_reg == bit_rate) begin
txd_bit_ctr_rst = 1;
etx_ctrl_new = ETX_BITS;
etx_ctrl_we = 1;
end
else begin
txd_bitrate_ctr_inc = 1;
end
end
ETX_BITS: begin
if (txd_bitrate_ctr_reg < bit_rate) begin
txd_bitrate_ctr_inc = 1;
end
else begin
txd_bitrate_ctr_rst = 1;
if (txd_bit_ctr_reg == data_bits) begin
txd_new = 1;
txd_we = 1;
etx_ctrl_new = ETX_STOP;
etx_ctrl_we = 1;
end
else begin
txd_new = txd_byte_reg[txd_bit_ctr_reg];
txd_we = 1;
txd_bit_ctr_inc = 1;
end
end
end
ETX_STOP: begin
txd_bitrate_ctr_inc = 1;
if (txd_bitrate_ctr_reg == bit_rate * stop_bits) begin
txd_ready_new = 1;
txd_ready_we = 1;
etx_ctrl_new = ETX_IDLE;
etx_ctrl_we = 1;
end
end
default: begin
end
endcase // case (etx_ctrl_reg)
end // external_tx_engine
endmodule // uart
//======================================================================
// EOF uart.v
//======================================================================

View File

@ -0,0 +1,179 @@
//======================================================================
//
// uart_fifo.v
// -----------
// FIFO for rx and tx data buffering in the UART.
//
//
// Author: Joachim Strombergson
// Copyright (c) 2022, Tillitis AB
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================
module uart_fifo(
input wire clk,
input wire reset_n,
input wire in_syn,
input wire [7 : 0] in_data,
output wire in_ack,
output wire out_syn,
output wire [7 : 0] out_data,
input wire out_ack
);
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg [7 : 0] fifo_mem [0 : 255];
reg fifo_mem_we;
reg [7: 0] in_ptr_reg;
reg [7: 0] in_ptr_new;
reg in_ptr_inc;
reg in_ptr_we;
reg [7: 0] out_ptr_reg;
reg [7: 0] out_ptr_new;
reg out_ptr_inc;
reg out_ptr_we;
reg [7: 0] byte_ctr_reg;
reg [7: 0] byte_ctr_new;
reg byte_ctr_inc;
reg byte_ctr_dec;
reg byte_ctr_we;
reg in_ack_reg;
reg in_ack_new;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign in_ack = in_ack_reg;
assign out_syn = |byte_ctr_reg;
assign out_data = fifo_mem[out_ptr_reg];
//----------------------------------------------------------------
// reg_update
//----------------------------------------------------------------
always @ (posedge clk)
begin: reg_update
if (!reset_n) begin
in_ptr_reg <= 8'h0;
out_ptr_reg <= 8'h0;
byte_ctr_reg <= 8'h0;
in_ack_reg <= 1'h0;
end
else begin
in_ack_reg <= in_ack_new;
if (fifo_mem_we) begin
fifo_mem[in_ptr_reg] <= in_data;
end
if (in_ptr_we) begin
in_ptr_reg <= in_ptr_new;
end
if (out_ptr_we) begin
out_ptr_reg <= out_ptr_new;
end
if (byte_ctr_we) begin
byte_ctr_reg <= byte_ctr_new;
end
end
end // reg_update
//----------------------------------------------------------------
// byte_ctr
//----------------------------------------------------------------
always @*
begin : byte_ctr
byte_ctr_new = 8'h0;
byte_ctr_we = 1'h0;
if ((byte_ctr_inc) && (!byte_ctr_dec)) begin
byte_ctr_new = byte_ctr_reg + 1'h1;
byte_ctr_we = 1'h1;
end
else if ((!byte_ctr_inc) && (byte_ctr_dec)) begin
byte_ctr_new = byte_ctr_reg - 1'h1;
byte_ctr_we = 1'h1;
end
end
//----------------------------------------------------------------
// in_logic
//----------------------------------------------------------------
always @*
begin : in_logic
fifo_mem_we = 1'h0;
in_ack_new = 1'h0;
byte_ctr_inc = 1'h0;
in_ptr_new = in_ptr_reg + 1'h1;
in_ptr_we = 1'h0;
if ((in_syn) && (!in_ack) && (byte_ctr_reg < 8'hff)) begin
fifo_mem_we = 1'h1;
in_ack_new = 1'h1;
byte_ctr_inc = 1'h1;
in_ptr_we = 1'h1;
end
end
//----------------------------------------------------------------
// out_logic
//----------------------------------------------------------------
always @*
begin : out_logic
byte_ctr_dec = 1'h0;
out_ptr_new = out_ptr_reg + 1'h1;
out_ptr_we = 1'h0;
if ((out_ack) && (byte_ctr_reg > 8'h0)) begin
byte_ctr_dec = 1'h1;
out_ptr_we = 1'h1;
end
end
endmodule // uart_fifo
//======================================================================
// EOF uart_fifo.v
//======================================================================

View File

@ -0,0 +1,389 @@
//======================================================================
//
// tb_uart.v
// ---------
// Testbench for the UART core.
//
//
// Author: Joachim Strombergson
// Copyright (c) 2014, Secworks Sweden AB
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================
module tb_uart();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 0;
parameter VERBOSE = 0;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = CLK_HALF_PERIOD * 2;
//----------------------------------------------------------------
// Register and Wire declarations.
//----------------------------------------------------------------
reg [31 : 0] cycle_ctr;
reg [31 : 0] error_ctr;
reg [31 : 0] tc_ctr;
reg tb_clk;
reg tb_reset_n;
reg tb_rxd;
wire tb_txd;
wire tb_rxd_syn;
wire [7 : 0] tb_rxd_data;
wire tb_rxd_ack;
wire tb_txd_syn;
wire [7 : 0] tb_txd_data;
wire tb_txd_ack;
reg tb_cs;
reg tb_we;
reg [7 : 0] tb_address;
reg [31 : 0] tb_write_data;
wire [31 : 0] tb_read_data;
wire tb_error;
wire [7 : 0] tb_debug;
reg txd_state;
//----------------------------------------------------------------
// Device Under Test.
//----------------------------------------------------------------
uart dut(
.clk(tb_clk),
.reset_n(tb_reset_n),
.rxd(tb_rxd),
.txd(tb_txd),
.rxd_syn(tb_rxd_syn),
.rxd_data(tb_rxd_data),
.rxd_ack(tb_rxd_ack),
// Internal transmit interface.
.txd_syn(tb_txd_syn),
.txd_data(tb_txd_data),
.txd_ack(tb_txd_ack),
// API interface.
.cs(tb_cs),
.we(tb_we),
.address(tb_address),
.write_data(tb_write_data),
.read_data(tb_read_data),
.error(tb_error),
.debug(tb_debug)
);
//----------------------------------------------------------------
// Concurrent assignments.
//----------------------------------------------------------------
// We connect the internal facing ports on the dut together.
assign tb_txd_syn = tb_rxd_syn;
assign tb_txd_data = tb_rxd_data;
assign tb_rxd_ack = tb_txd_ack;
//----------------------------------------------------------------
// clk_gen
//
// Clock generator process.
//----------------------------------------------------------------
always
begin : clk_gen
#CLK_HALF_PERIOD tb_clk = !tb_clk;
end // clk_gen
//----------------------------------------------------------------
// sys_monitor
//----------------------------------------------------------------
always
begin : sys_monitor
#(CLK_PERIOD);
if (DEBUG)
begin
dump_rx_state();
dump_tx_state();
$display("");
end
if (VERBOSE)
begin
$display("cycle: 0x%016x", cycle_ctr);
end
cycle_ctr = cycle_ctr + 1;
end
//----------------------------------------------------------------
// tx_monitor
//
// Observes what happens on the dut tx port and reports it.
//----------------------------------------------------------------
always @*
begin : tx_monitor
if ((!tb_txd) && txd_state)
begin
$display("txd going low.");
txd_state = 0;
end
if (tb_txd && (!txd_state))
begin
$display("txd going high");
txd_state = 1;
end
end
//----------------------------------------------------------------
// dump_dut_state()
//
// Dump the state of the dut when needed.
//----------------------------------------------------------------
task dump_dut_state;
begin
$display("State of DUT");
$display("------------");
$display("Inputs and outputs:");
$display("rxd = 0x%01x, txd = 0x%01x,",
dut.core.rxd, dut.core.txd);
$display("");
$display("Sample and data registers:");
$display("rxd_reg = 0x%01x, rxd_byte_reg = 0x%01x",
dut.core.rxd_reg, dut.core.rxd_byte_reg);
$display("");
$display("Counters:");
$display("rxd_bit_ctr_reg = 0x%01x, rxd_bitrate_ctr_reg = 0x%02x",
dut.core.rxd_bit_ctr_reg, dut.core.rxd_bitrate_ctr_reg);
$display("");
$display("Control signals and FSM state:");
$display("erx_ctrl_reg = 0x%02x",
dut.core.erx_ctrl_reg);
$display("");
end
endtask // dump_dut_state
//----------------------------------------------------------------
// dump_rx_state()
//
// Dump the state of the rx engine.
//----------------------------------------------------------------
task dump_rx_state;
begin
$display("rxd = 0x%01x, rxd_reg = 0x%01x, rxd_byte_reg = 0x%01x, rxd_bit_ctr_reg = 0x%01x, rxd_bitrate_ctr_reg = 0x%02x, rxd_syn = 0x%01x, erx_ctrl_reg = 0x%02x",
dut.core.rxd, dut.core.rxd_reg, dut.core.rxd_byte_reg, dut.core.rxd_bit_ctr_reg,
dut.core.rxd_bitrate_ctr_reg, dut.core.rxd_syn, dut.core.erx_ctrl_reg);
end
endtask // dump_dut_state
//----------------------------------------------------------------
// dump_tx_state()
//
// Dump the state of the tx engine.
//----------------------------------------------------------------
task dump_tx_state;
begin
$display("txd = 0x%01x, txd_reg = 0x%01x, txd_byte_reg = 0x%01x, txd_bit_ctr_reg = 0x%01x, txd_bitrate_ctr_reg = 0x%02x, txd_ack = 0x%01x, etx_ctrl_reg = 0x%02x",
dut.core.txd, dut.core.txd_reg, dut.core.txd_byte_reg, dut.core.txd_bit_ctr_reg,
dut.core.txd_bitrate_ctr_reg, dut.core.txd_ack, dut.core.etx_ctrl_reg);
end
endtask // dump_dut_state
//----------------------------------------------------------------
// reset_dut()
//----------------------------------------------------------------
task reset_dut;
begin
$display("*** Toggle reset.");
tb_reset_n = 0;
#(2 * CLK_PERIOD);
tb_reset_n = 1;
end
endtask // reset_dut
//----------------------------------------------------------------
// init_sim()
//
// Initialize all counters and testbed functionality as well
// as setting the DUT inputs to defined values.
//----------------------------------------------------------------
task init_sim;
begin
cycle_ctr = 0;
error_ctr = 0;
tc_ctr = 0;
tb_clk = 0;
tb_reset_n = 1;
tb_rxd = 1;
tb_cs = 0;
tb_we = 0;
tb_address = 8'h00;
tb_write_data = 32'h00000000;
txd_state = 1;
end
endtask // init_sim
//----------------------------------------------------------------
// transmit_byte
//
// Transmit a byte of data to the DUT receive port.
//----------------------------------------------------------------
task transmit_byte(input [7 : 0] data);
integer i;
begin
$display("*** Transmitting byte 0x%02x to the dut.", data);
#10;
// Start bit
$display("*** Transmitting start bit.");
tb_rxd = 0;
#(CLK_PERIOD * dut.DEFAULT_BIT_RATE);
// Send the bits LSB first.
for (i = 0 ; i < 8 ; i = i + 1)
begin
$display("*** Transmitting data[%1d] = 0x%01x.", i, data[i]);
tb_rxd = data[i];
#(CLK_PERIOD * dut.DEFAULT_BIT_RATE);
end
// Send two stop bits. I.e. two bit times high (mark) value.
$display("*** Transmitting two stop bits.");
tb_rxd = 1;
#(2 * CLK_PERIOD * dut.DEFAULT_BIT_RATE * dut.DEFAULT_STOP_BITS);
$display("*** End of transmission.");
end
endtask // transmit_byte
//----------------------------------------------------------------
// check_transmit
//
// Transmits a byte and checks that it was captured internally
// by the dut.
//----------------------------------------------------------------
task check_transmit(input [7 : 0] data);
begin
tc_ctr = tc_ctr + 1;
transmit_byte(data);
if (dut.core.rxd_byte_reg == data)
begin
$display("*** Correct data: 0x%01x captured by the dut.",
dut.core.rxd_byte_reg);
end
else
begin
$display("*** Incorrect data: 0x%01x captured by the dut Should be: 0x%01x.",
dut.core.rxd_byte_reg, data);
error_ctr = error_ctr + 1;
end
end
endtask // check_transmit
//----------------------------------------------------------------
// test_transmit
//
// Transmit a number of test bytes to the dut.
//----------------------------------------------------------------
task test_transmit;
begin
check_transmit(8'h55);
check_transmit(8'h42);
check_transmit(8'hde);
check_transmit(8'had);
end
endtask // test_transmit
//----------------------------------------------------------------
// display_test_result()
//
// Display the accumulated test results.
//----------------------------------------------------------------
task display_test_result;
begin
if (error_ctr == 0)
begin
$display("*** All %02d test cases completed successfully", tc_ctr);
end
else
begin
$display("*** %02d test cases did not complete successfully.", error_ctr);
end
end
endtask // display_test_result
//----------------------------------------------------------------
// uart_test
// The main test functionality.
//----------------------------------------------------------------
initial
begin : uart_test
$display(" -- Testbench for uart core started --");
init_sim();
dump_dut_state();
reset_dut();
dump_dut_state();
test_transmit();
display_test_result();
$display("*** Simulation done.");
$finish;
end // uart_test
endmodule // tb_uart
//======================================================================
// EOF tb_uart.v
//======================================================================

View File

@ -0,0 +1,7 @@
# uds
Unique Device Secret core
## Introduction
This core store and protect the Unique Device Secret. The
storage is implemented in discrete registers. The contents can be read once between chip reset, and only if the system is in not in application access mode.

View File

@ -0,0 +1,129 @@
//======================================================================
//
// uds.v
// --------
// Top level wrapper for the uds core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module uds(
input wire clk,
input wire reset_n,
output wire fw_app_mode,
input wire cs,
input wire [7 : 0] address,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_UDS_FIRST = 8'h10;
localparam ADDR_UDS_LAST = 8'h17;
localparam CORE_NAME0 = 32'h7564735f; // "uds_"
localparam CORE_NAME1 = 32'h6d656d20; // "mem "
localparam CORE_VERSION = 32'h00000001;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg [31 : 0] uds_reg [0 : 7];
initial $readmemh(`UDS_HEX, uds_reg);
reg uds_rd_reg [0 : 7];
reg uds_rd_we;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
reg [31 : 0] tmp_read_data;
reg tmp_ready;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = tmp_ready;
//----------------------------------------------------------------
// reg_update
//----------------------------------------------------------------
always @ (posedge clk)
begin : reg_update
integer i;
if (!reset_n) begin
for (i = 0 ; i < 8 ; i = i + 1) begin
uds_rd_reg[i] <= 1'h0;;
end
end
else begin
if (uds_rd_we) begin
uds_rd_reg[address[2 : 0]] <= 1'h1;
end
end
end // reg_update
//----------------------------------------------------------------
// api
//
// The interface command decoding logic.
//----------------------------------------------------------------
always @*
begin : api
uds_rd_we = 1'h0;
tmp_read_data = 32'h0;
tmp_ready = 1'h0;
if (cs) begin
tmp_ready = 1'h1;
if (address == ADDR_NAME0) begin
tmp_read_data = CORE_NAME0;
end
if (address == ADDR_NAME1) begin
tmp_read_data = CORE_NAME1;
end
if (address == ADDR_VERSION) begin
tmp_read_data = CORE_VERSION;
end
if ((address >= ADDR_UDS_FIRST) && (address <= ADDR_UDS_LAST)) begin
if (!fw_app_mode) begin
if (uds_rd_reg[address[2 : 0]] == 1'h0) begin
tmp_read_data = uds_reg[address[2 : 0]];
uds_rd_we = 1'h1;
end
end
end
end
end
endmodule // uds
//======================================================================
// EOF uds.v
//======================================================================

View File

@ -0,0 +1,300 @@
//======================================================================
//
// tb_uds.v
// -----------
// Testbench for the UDS core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module tb_uds();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 1;
parameter DUMP_WAIT = 0;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_UDS_FIRST = 8'h10;
localparam ADDR_UDS_LAST = 8'h17;
//----------------------------------------------------------------
// Register and Wire declarations.
//----------------------------------------------------------------
reg [31 : 0] cycle_ctr;
reg [31 : 0] error_ctr;
reg [31 : 0] tc_ctr;
reg tb_monitor;
reg tb_clk;
reg tb_reset_n;
reg tb_cs;
reg [7 : 0] tb_address;
wire [31 : 0] tb_read_data;
reg [31 : 0] read_data;
//----------------------------------------------------------------
// Device Under Test.
//----------------------------------------------------------------
uds dut(
.clk(tb_clk),
.reset_n(tb_reset_n),
.cs(tb_cs),
.address(tb_address),
.read_data(tb_read_data)
);
//----------------------------------------------------------------
// clk_gen
//
// Always running clock generator process.
//----------------------------------------------------------------
always
begin : clk_gen
#CLK_HALF_PERIOD;
tb_clk = !tb_clk;
end // clk_gen
//----------------------------------------------------------------
// sys_monitor()
//
// An always running process that creates a cycle counter and
// conditionally displays information about the DUT.
//----------------------------------------------------------------
always
begin : sys_monitor
cycle_ctr = cycle_ctr + 1;
#(CLK_PERIOD);
if (tb_monitor)
begin
dump_dut_state();
end
end
//----------------------------------------------------------------
// dump_dut_state()
//
// Dump the state of the dump when needed.
//----------------------------------------------------------------
task dump_dut_state;
begin : dump_dut_state
integer i;
$display("State of DUT");
$display("------------");
$display("Cycle: %08d", cycle_ctr);
for (i = 0 ; i < 8 ; i = i + 1) begin
$display("uds_reg[%1d]: 0x%08x, uds_rd_reg[%1d]: 0x%1x",
i, dut.uds_reg[i], i, dut.uds_rd_reg[i]);
end
$display("");
$display("");
end
endtask // dump_dut_state
//----------------------------------------------------------------
// reset_dut()
//
// Toggle reset to put the DUT into a well known state.
//----------------------------------------------------------------
task reset_dut;
begin
$display("--- Toggle reset.");
tb_reset_n = 0;
#(2 * CLK_PERIOD);
tb_reset_n = 1;
end
endtask // reset_dut
//----------------------------------------------------------------
// display_test_result()
//
// Display the accumulated test results.
//----------------------------------------------------------------
task display_test_result;
begin
if (error_ctr == 0)
begin
$display("--- All %02d test cases completed successfully", tc_ctr);
end
else
begin
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
tc_ctr, error_ctr);
end
end
endtask // display_test_result
//----------------------------------------------------------------
// init_sim()
//
// Initialize all counters and testbed functionality as well
// as setting the DUT inputs to defined values.
//----------------------------------------------------------------
task init_sim;
begin
cycle_ctr = 0;
error_ctr = 0;
tc_ctr = 0;
tb_monitor = 0;
tb_clk = 1'h0;
tb_reset_n = 1'h1;
tb_cs = 1'h0;
tb_address = 8'h0;
end
endtask // init_sim
//----------------------------------------------------------------
// read_word()
//
// Read a data word from the given address in the DUT.
// the word read will be available in the global variable
// read_data.
//----------------------------------------------------------------
task read_word(input [11 : 0] address);
begin
tb_address = address;
tb_cs = 1;
#(CLK_PERIOD);
read_data = tb_read_data;
tb_cs = 0;
if (DEBUG)
begin
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
$display("");
end
end
endtask // read_word
//----------------------------------------------------------------
// test1()
//----------------------------------------------------------------
task test1;
begin
tc_ctr = tc_ctr + 1;
tb_monitor = 1'h0;
$display("");
$display("--- test1: started.");
$display("--- test1: Reading NAME and version info.");
read_word(ADDR_NAME0);
read_word(ADDR_NAME1);
read_word(ADDR_VERSION);
$display("--- test1: Dumping DUT state to show UDS contents");
dump_dut_state();
$display("--- test1: Reading UDS words.");
read_word(ADDR_UDS_FIRST + 0);
read_word(ADDR_UDS_FIRST + 1);
read_word(ADDR_UDS_FIRST + 2);
$display("--- test1: Dumping state again to see read bits.");
dump_dut_state();
$display("--- test1: Reading rest of the words.");
read_word(ADDR_UDS_FIRST + 3);
read_word(ADDR_UDS_FIRST + 4);
read_word(ADDR_UDS_FIRST + 5);
read_word(ADDR_UDS_FIRST + 6);
read_word(ADDR_UDS_FIRST + 7);
$display("--- test1: Dumping state again to see read bits.");
dump_dut_state();
$display("--- test1: Reading UDS words again.");
read_word(ADDR_UDS_FIRST + 0);
read_word(ADDR_UDS_FIRST + 1);
read_word(ADDR_UDS_FIRST + 2);
read_word(ADDR_UDS_FIRST + 3);
read_word(ADDR_UDS_FIRST + 4);
read_word(ADDR_UDS_FIRST + 5);
read_word(ADDR_UDS_FIRST + 6);
read_word(ADDR_UDS_FIRST + 7);
$display("--- test1: Resetting DUT.");
reset_dut();
$display("--- test1: Dumping state again to see read bits.");
dump_dut_state();
$display("--- test1: Reading UDS words.");
read_word(ADDR_UDS_FIRST + 0);
read_word(ADDR_UDS_FIRST + 1);
read_word(ADDR_UDS_FIRST + 2);
read_word(ADDR_UDS_FIRST + 3);
read_word(ADDR_UDS_FIRST + 4);
read_word(ADDR_UDS_FIRST + 5);
read_word(ADDR_UDS_FIRST + 6);
read_word(ADDR_UDS_FIRST + 7);
$display("--- test1: Reading UDS words again.");
read_word(ADDR_UDS_FIRST + 0);
read_word(ADDR_UDS_FIRST + 1);
read_word(ADDR_UDS_FIRST + 2);
read_word(ADDR_UDS_FIRST + 3);
read_word(ADDR_UDS_FIRST + 4);
read_word(ADDR_UDS_FIRST + 5);
read_word(ADDR_UDS_FIRST + 6);
read_word(ADDR_UDS_FIRST + 7);
$display("--- test1: completed.");
$display("");
end
endtask // tes1
//----------------------------------------------------------------
// uds_test
//----------------------------------------------------------------
initial
begin : uds_test
$display("");
$display(" -= Testbench for uds started =-");
$display(" ===========================");
$display("");
init_sim();
reset_dut();
test1();
display_test_result();
$display("");
$display(" -= Testbench for uds started =-");
$display(" ===========================");
$display("");
$finish;
end // uds_test
endmodule // tb_uds
//======================================================================
// EOF tb_uds.v
//======================================================================

View File

@ -0,0 +1,55 @@
#===================================================================
#
# Makefile
# --------
# Makefile for building the UDS core.
#
#
# Author: Joachim Strombergson
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#===================================================================
TOP_SRC=../rtl/uds.v
TB_TOP_SRC =../tb/tb_uds.v
CC = iverilog
CC_FLAGS = -Wall
LINT = verilator
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
all: top.sim
top.sim: $(TB_TOP_SRC) $(TOP_SRC)
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
sim-top: top.sim
./top.sim
lint-top: $(TOP_SRC)
$(LINT) $(LINT_FLAGS) $(TOP_SRC)
clean:
rm -f top.sim
help:
@echo "Build system for simulation of UDS core"
@echo ""
@echo "Supported targets:"
@echo "------------------"
@echo "top.sim: Build top level simulation target."
@echo "sim-top: Run top level simulation."
@echo "lint-top: Lint top rtl source files."
@echo "clean: Delete all built files."
#===================================================================
# EOF Makefile
#===================================================================

View File

@ -0,0 +1,39 @@
#=======================================================================
#
# application_fpga_mta1_usb_dev.pcf
# ---------------------------------
# Pin constraints file for the Application FPGA design used on the
# Mullvad MTA1_USB_DEV board.
#
#
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#=======================================================================
# UART.
set_io interface_rx 15
set_io interface_tx 16
#set_io interface_cts 27
#set_io interface_rts 28
# Touch sense, mapped to a switch.
set_io touch_event 23
# GPIOs.
set_io app_gpio1 26
set_io app_gpio2 27
set_io app_gpio3 31
set_io app_gpio4 32
# LEDs
set_io led_r 41
set_io led_g 40
set_io led_b 39
#=======================================================================
# EOF application_fpga_usb_dev.pcf
#=======================================================================

View File

@ -0,0 +1,39 @@
#=======================================================================
#
# application_fpga_usb_v1.pcf
# ---------------------------
# Pin constraints file for the Application FPGA design to be used
# on the MTA1-USB-V1 board with the CH552 MCU used as a USB-serial chip.
#
#
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#=======================================================================
# UART.
set_io interface_rx 26
set_io interface_tx 25
# set_io interface_cts 27
# set_io interface_rts 28
# Touch sense.
set_io touch_event 6
# GPIOs.
set_io app_gpio1 36
set_io app_gpio2 38
set_io app_gpio3 45
set_io app_gpio4 46
# LEDs
set_io led_r 39
set_io led_g 40
set_io led_b 41
#=======================================================================
# EOF application_fpga_ch552.pcf
#=======================================================================

View File

@ -0,0 +1,2 @@
00010203
04050607

View File

@ -0,0 +1,8 @@
80808080
91919191
a2a2a2a2
b3b3b3b3
c4c4c4c4
d5d5d5d5
e6e6e6e6
f7f7f7f7

View File

@ -0,0 +1,15 @@
BasedOnStyle: LLVM
UseTab: Always
IndentWidth: 8
TabWidth: 8
IncludeBlocks: Preserve
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortEnumsOnASingleLine: false

View File

@ -0,0 +1,4 @@
.PHONY: fmt
fmt:
# Uses ../.clang-format
clang-format --verbose -i main.c lib.h lib.c proto.h proto.c types.h

View File

@ -0,0 +1,53 @@
# Tillitis Key firmware
## Build the firmware
You need Clang with 32 bit RISC-V support. You can check this with:
```
$ llc --version|grep riscv32
riscv32 - 32-bit RISC-V
```
or just try building.
Build the FPGA bitstream with the firmware using `make` in the
`hw/application_fpga` directory.
If your available `objcopy` and `size` commands is anything other than
the default `llvm-objcopy-14` and `llvm-size-14` define `OBJCOPY` and
`SIZE` to whatever they're called on your system.
## Using QEMU
Checkout the `mta1` branch of [our version of the
qemu](https://github.com/tillitis/qemu) and build:
```
$ git clone -b mta1 https://github.com/tillitis/qemu
$ mkdir qemu/build
$ cd qemu/build
$ ../configure --target-list=riscv32-softmmu
$ make -j $(nproc)
```
Run it like this:
```
$ /path/to/qemu/build/qemu-system-riscv32 -nographic -M mta1_mkdf,fifo=chrid -bios firmware \
-chardev pty,id=chrid
```
This attaches the FIFO to a tty, something like `/dev/pts/16` which
you can use with host software to talk to the firmware.
To quit QEMU you can use: `Ctrl-a x` (see `Ctrl-a ?` for other commands).
Debugging? Use the HTIF console by removing `-DNOCONSOLE` from the
`CFLAGS` and using the helper functions in `lib.c` for printf-like
debugging.
You can also use the qemu monitor for debugging, e.g. `info
registers`, or run qemu with `-d in_asm` or `-d trace:riscv_trap`.
Happy hacking!

View File

@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

View File

@ -0,0 +1,11 @@
# blake2s
A Blake2s implementation taken from Joachim Strömbergson's
https://github.com/secworks/blake2s
Specifically from
https://github.com/secworks/blake2s/tree/master/src/model
Minor local changes for build purposes.

View File

@ -0,0 +1,352 @@
//======================================================================
//
// blake2s.c
// ---------
//
// A simple blake2s Reference Implementation.
//======================================================================
#include "../types.h"
#include "../lib.h"
#include "blake2s.h"
// Dummy printf() for verbose mode
static void printf(const char *format, ...)
{
}
#define VERBOSE 0
#define SHOW_V 0
#define SHOW_M_WORDS 0
// Cyclic right rotation.
#ifndef ROTR32
#define ROTR32(x, y) (((x) >> (y)) ^ ((x) << (32 - (y))))
#endif
// Little-endian byte access.
#define B2S_GET32(p) \
(((uint32_t) ((uint8_t *) (p))[0]) ^ \
(((uint32_t) ((uint8_t *) (p))[1]) << 8) ^ \
(((uint32_t) ((uint8_t *) (p))[2]) << 16) ^ \
(((uint32_t) ((uint8_t *) (p))[3]) << 24))
// Initialization Vector.
static const uint32_t blake2s_iv[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
//------------------------------------------------------------------
//------------------------------------------------------------------
void print_v(uint32_t *v) {
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[0], v[1], v[2], v[3]);
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[4], v[5], v[6], v[7]);
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[8], v[9], v[10], v[11]);
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[12], v[13], v[14], v[15]);
printf("\n");
}
//------------------------------------------------------------------
// print_ctx()
// Print the contents of the context data structure.
//------------------------------------------------------------------
void print_ctx(blake2s_ctx *ctx) {
printf("Chained state (h):\n");
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x, ",
ctx->h[0], ctx->h[1], ctx->h[2], ctx->h[3]);
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x",
ctx->h[4], ctx->h[5], ctx->h[6], ctx->h[7]);
printf("\n");
printf("Byte counter (t):\n");
printf("0x%08x, 0x%08x", ctx->t[0], ctx->t[1]);
printf("\n");
printf("\n");
}
//------------------------------------------------------------------
// B2S_G macro redefined as a G function.
// Allows us to output intermediate values for debugging.
//------------------------------------------------------------------
void G(uint32_t *v, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t y) {
if (VERBOSE) {
printf("G started.\n");
}
if (SHOW_V) {
printf("v before processing:\n");
print_v(&v[0]);
}
if (SHOW_M_WORDS) {
printf("x: 0x%08x, y: 0x%08x\n", x, y);
}
v[a] = v[a] + v[b] + x;
v[d] = ROTR32(v[d] ^ v[a], 16);
v[c] = v[c] + v[d];
v[b] = ROTR32(v[b] ^ v[c], 12);
v[a] = v[a] + v[b] + y;
v[d] = ROTR32(v[d] ^ v[a], 8);
v[c] = v[c] + v[d];
v[b] = ROTR32(v[b] ^ v[c], 7);
if (SHOW_V) {
printf("v after processing:\n");
print_v(&v[0]);
}
if (VERBOSE) {
printf("G completed.\n\n");
}
}
//------------------------------------------------------------------
// Compression function. "last" flag indicates last block.
//------------------------------------------------------------------
static void blake2s_compress(blake2s_ctx *ctx, int last)
{
const uint8_t sigma[10][16] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}
};
int i;
uint32_t v[16], m[16];
if (VERBOSE) {
printf("blake2s_compress started.\n");
}
// init work variables
for (i = 0; i < 8; i++) {
v[i] = ctx->h[i];
v[i + 8] = blake2s_iv[i];
}
// low 32 bits of offset
// high 32 bits
if (VERBOSE) {
printf("t[0]: 0x%08x, t[1]: 0x%08x\n", ctx->t[0], ctx->t[1]);
}
v[12] ^= ctx->t[0];
v[13] ^= ctx->t[1];
// last block flag set ?
if (last) {
v[14] = ~v[14];
}
// get little-endian words
for (i = 0; i < 16; i++) {
m[i] = B2S_GET32(&ctx->b[4 * i]);
}
if (VERBOSE) {
printf("v before G processing:\n");
print_v(&v[0]);
}
// Ten rounds of the G function applied on rows, diagonal.
for (i = 0; i < 10; i++) {
if (VERBOSE) {
printf("Round %02d:\n", (i + 1));
printf("Row processing started.\n");
}
G(&v[0], 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
G(&v[0], 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
G(&v[0], 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
G(&v[0], 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
if (VERBOSE) {
printf("Row processing completed.\n");
printf("Diagonal processing started.\n");
}
G(&v[0], 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
G(&v[0], 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
G(&v[0], 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
G(&v[0], 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
if (VERBOSE) {
printf("Diagonal processing completed.\n");
printf("\n");
}
}
if (VERBOSE) {
printf("v after G processing:\n");
print_v(&v[0]);
}
// Update the hash state.
for (i = 0; i < 8; ++i) {
ctx->h[i] ^= v[i] ^ v[i + 8];
}
if (VERBOSE) {
printf("blake2s_compress completed.\n");
}
}
//------------------------------------------------------------------
// Initialize the hashing context "ctx" with optional key "key".
// 1 <= outlen <= 32 gives the digest size in bytes.
// Secret key (also <= 32 bytes) is optional (keylen = 0).
//------------------------------------------------------------------
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
const void *key, size_t keylen) // (keylen=0: no key)
{
size_t i;
if (VERBOSE) {
printf("blake2s_init started.\n");
printf("Context before blake2s_init processing:\n");
print_ctx(ctx);
}
if (outlen == 0 || outlen > 32 || keylen > 32)
return -1; // illegal parameters
for (i = 0; i < 8; i++) // state, "param block"
ctx->h[i] = blake2s_iv[i];
ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
ctx->t[0] = 0; // input count low word
ctx->t[1] = 0; // input count high word
ctx->c = 0; // pointer within buffer
ctx->outlen = outlen;
for (i = keylen; i < 64; i++) // zero input block
ctx->b[i] = 0;
if (keylen > 0) {
blake2s_update(ctx, key, keylen);
ctx->c = 64; // at the end
}
if (VERBOSE) {
printf("Context after blake2s_init processing:\n");
print_ctx(ctx);
printf("blake2s_init completed.\n");
}
return 0;
}
//------------------------------------------------------------------
// Add "inlen" bytes from "in" into the hash.
//------------------------------------------------------------------
void blake2s_update(blake2s_ctx *ctx,
const void *in, size_t inlen) // data bytes
{
size_t i;
if (VERBOSE) {
printf("blake2s_update started.\n");
printf("Context before blake2s_update processing:\n");
print_ctx(ctx);
}
for (i = 0; i < inlen; i++) {
if (ctx->c == 64) { // buffer full ?
ctx->t[0] += ctx->c; // add counters
if (ctx->t[0] < ctx->c) // carry overflow ?
ctx->t[1]++; // high word
blake2s_compress(ctx, 0); // compress (not last)
ctx->c = 0; // counter to zero
}
ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
}
if (VERBOSE) {
printf("Context after blake2s_update processing:\n");
print_ctx(ctx);
printf("blake2s_update completed.\n");
}
}
//------------------------------------------------------------------
// Generate the message digest (size given in init).
// Result placed in "out".
//------------------------------------------------------------------
void blake2s_final(blake2s_ctx *ctx, void *out)
{
size_t i;
if (VERBOSE) {
printf("blake2s_final started.\n");
printf("Context before blake2s_final processing:\n");
print_ctx(ctx);
}
ctx->t[0] += ctx->c; // mark last block offset
// carry overflow
// high word
if (ctx->t[0] < ctx->c) {
ctx->t[1]++;
}
// fill up with zeros
// final block flag = 1
while (ctx->c < 64) {
ctx->b[ctx->c++] = 0;
}
blake2s_compress(ctx, 1);
// little endian convert and store
for (i = 0; i < ctx->outlen; i++) {
((uint8_t *) out)[i] =
(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;
}
if (VERBOSE) {
printf("Context after blake2s_final processing:\n");
print_ctx(ctx);
printf("blake2s_final completed.\n");
}
}
//------------------------------------------------------------------
// Convenience function for all-in-one computation.
//------------------------------------------------------------------
int blake2s(void *out, size_t outlen,
const void *key, size_t keylen,
const void *in, size_t inlen)
{
blake2s_ctx ctx;
if (blake2s_init(&ctx, outlen, key, keylen))
return -1;
blake2s_update(&ctx, in, inlen);
blake2s_final(&ctx, out);
return 0;
}
//======================================================================
//======================================================================

View File

@ -0,0 +1,38 @@
// blake2s.h
// BLAKE2s Hashing Context and API Prototypes
#ifndef BLAKE2S_H
#define BLAKE2S_H
#include "../types.h"
// state context
typedef struct {
uint8_t b[64]; // input buffer
uint32_t h[8]; // chained state
uint32_t t[2]; // total number of bytes
size_t c; // pointer for b[]
size_t outlen; // digest size
} blake2s_ctx;
// Initialize the hashing context "ctx" with optional key "key".
// 1 <= outlen <= 32 gives the digest size in bytes.
// Secret key (also <= 32 bytes) is optional (keylen = 0).
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
const void *key, size_t keylen); // secret key
// Add "inlen" bytes from "in" into the hash.
void blake2s_update(blake2s_ctx *ctx, // context
const void *in, size_t inlen); // data to be hashed
// Generate the message digest (size given in init).
// Result placed in "out".
void blake2s_final(blake2s_ctx *ctx, void *out);
// All-in-one convenience function.
int blake2s(void *out, size_t outlen, // return buffer for digest
const void *key, size_t keylen, // optional secret key
const void *in, size_t inlen); // data to be hashed
#endif

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
MEMORY
{
/* TODO ROM size should be adjusted, RAM should be ok. */
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128 KB */
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}
SECTIONS
{
.text.init :
{
*(.text.init)
} >ROM
.htif :
{
. = ALIGN(0x00000000);
*(.htif)
} >ROM
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.srodata) /* .rodata sections (constants, strings, etc.) */
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} >ROM
.data : AT (_etext)
{
. = ALIGN(4);
_sdata = .;
. = ALIGN(4);
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.sdata) /* .sdata sections */
*(.sdata*) /* .sdata* sections */
. = ALIGN(4);
_edata = .;
} >RAM
/* Uninitialized data section */
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
/* Init stack to _ebss + size */
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "lib.h"
#include "types.h"
#if NOCONSOLE
void putc(int ch)
{
}
void lf()
{
}
void puthex(uint8_t c)
{
}
void putinthex(const uint32_t n)
{
}
int puts(const char *s)
{
return 0;
}
void hexdump(uint8_t *buf, int len)
{
}
#else
struct {
uint32_t arr[2];
} volatile tohost __attribute__((section(".htif")));
struct {
uint32_t arr[2];
} volatile fromhost __attribute__((section(".htif")));
static void htif_send(uint8_t dev, uint8_t cmd, int64_t data)
{
/* endian neutral encoding with ordered 32-bit writes */
union {
uint32_t arr[2];
uint64_t val;
} encode = {.val = (uint64_t)dev << 56 | (uint64_t)cmd << 48 | data};
tohost.arr[0] = encode.arr[0];
tohost.arr[1] = encode.arr[1];
}
static void htif_set_tohost(uint8_t dev, uint8_t cmd, int64_t data)
{
/* send data with specified device and command */
while (tohost.arr[0]) {
asm volatile("" : : "r"(fromhost.arr[0]));
asm volatile("" : : "r"(fromhost.arr[1]));
}
htif_send(dev, cmd, data);
}
static int htif_putchar(int ch)
{
htif_set_tohost(1, 1, ch & 0xff);
return ch & 0xff;
}
int puts(const char *s)
{
while (*s)
htif_putchar(*s++);
return 0;
}
void hexdump(uint8_t *buf, int len)
{
uint8_t *row;
uint8_t *byte;
uint8_t *max;
row = buf;
max = &buf[len];
for (byte = 0; byte != max; row = byte) {
for (byte = row; byte != max && byte != (row + 16); byte++) {
puthex(*byte);
}
lf();
}
}
void putc(int ch)
{
htif_putchar(ch);
}
void lf()
{
htif_putchar('\n');
}
void puthex(uint8_t c)
{
unsigned int upper = (c >> 4) & 0xf;
unsigned int lower = c & 0xf;
htif_putchar(upper < 10 ? '0' + upper : 'a' - 10 + upper);
htif_putchar(lower < 10 ? '0' + lower : 'a' - 10 + lower);
}
void putinthex(const uint32_t n)
{
uint8_t buf[4];
memcpy(buf, &n, 4);
puts("0x");
for (int i = 3; i > -1; i--) {
puthex(buf[i]);
}
}
#endif
void *memset(void *dest, int c, unsigned n)
{
uint8_t *s = dest;
for (; n; n--, s++)
*s = c;
return dest;
}
__attribute__((used)) void *memcpy(void *dest, const void *src, unsigned n)
{
uint8_t *src_byte = (uint8_t *)src;
uint8_t *dest_byte = (uint8_t *)dest;
for (int i = 0; i < n; i++) {
dest_byte[i] = src_byte[i];
}
return dest;
}
__attribute__((used)) void *wordcpy(void *dest, const void *src, unsigned n)
{
uint32_t *src_word = (uint32_t *)src;
uint32_t *dest_word = (uint32_t *)dest;
for (int i = 0; i < n; i++) {
dest_word[i] = src_word[i];
}
return dest;
}
int memeq(void *dest, const void *src, unsigned n)
{
uint8_t *src_byte = (uint8_t *)src;
uint8_t *dest_byte = (uint8_t *)dest;
for (int i = 0; i < n; i++) {
if (dest_byte[i] != src_byte[i]) {
return 0;
}
}
return -1;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef LIB_H
#define LIB_H
#include "types.h"
void putc(int ch);
void lf();
void puthex(uint8_t c);
void putinthex(const uint32_t n);
int puts(const char *s);
void hexdump(uint8_t *buf, int len);
void *memset(void *dest, int c, unsigned n);
void *memcpy(void *dest, const void *src, unsigned n);
void *wordcpy(void *dest, const void *src, unsigned n);
int memeq(void *dest, const void *src, unsigned n);
#endif

View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../mta1_mkdf_mem.h"
#include "blake2s/blake2s.h"
#include "lib.h"
#include "proto.h"
#include "types.h"
// In RAM + above the stack (0x40010000)
#define APP_RAM_ADDR (MTA1_MKDF_RAM_BASE + 0x10000)
#define APP_MAX_SIZE 65536
// clang-format off
static volatile uint32_t *uds = (volatile uint32_t *)MTA1_MKDF_MMIO_UDS_FIRST;
static volatile uint32_t *switch_app = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_SWITCH_APP;
static volatile uint32_t *name0 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME0;
static volatile uint32_t *name1 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME1;
static volatile uint32_t *ver = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_VERSION;
static volatile uint32_t *cdi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_CDI_FIRST;
static volatile uint32_t *app_addr = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_APP_ADDR;
static volatile uint32_t *app_size = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_APP_SIZE;
#define LED_RED (1 << MTA1_MKDF_MMIO_MTA1_LED_R_BIT)
#define LED_GREEN (1 << MTA1_MKDF_MMIO_MTA1_LED_G_BIT)
#define LED_BLUE (1 << MTA1_MKDF_MMIO_MTA1_LED_B_BIT)
static void print_hw_version(uint32_t name0, uint32_t name1, uint32_t ver)
{
puts("Hello, I'm ");
putc(name0 >> 24);
putc(name0 >> 16);
putc(name0 >> 8);
putc(name0);
putc('-');
putc(name1 >> 24);
putc(name1 >> 16);
putc(name1 >> 8);
putc(name1);
putc(' ');
putinthex(ver);
lf();
}
// clang-format on
static void print_digest(uint8_t *md)
{
puts("The app digest:\n");
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 8; i++) {
puthex(md[i + 8 * j]);
}
lf();
}
lf();
}
int main()
{
uint32_t local_name0 = *name0;
uint32_t local_name1 = *name1;
uint32_t local_ver = *ver;
struct frame_header hdr; // Used in both directions
uint8_t cmd[CMDLEN_MAXBYTES];
uint8_t rsp[CMDLEN_MAXBYTES];
uint8_t *loadaddr = (uint8_t *)APP_RAM_ADDR;
int left = 0; // Bytes left to read
int nbytes = 0; // Bytes to write to memory
uint32_t local_app_size = 0;
uint8_t in;
uint8_t digest[32];
print_hw_version(local_name0, local_name1, local_ver);
for (;;) {
// blocking; fw flashing white while waiting for cmd
in = readbyte_ledflash(LED_RED | LED_BLUE | LED_GREEN, 500000);
if (parseframe(in, &hdr) == -1) {
puts("Couldn't parse header\n");
continue;
}
memset(cmd, 0, CMDLEN_MAXBYTES);
// Read firmware command: Blocks!
read(cmd, hdr.len);
// Is it for us?
if (hdr.endpoint != DST_FW) {
puts("Message not meant for us\n");
continue;
}
// Reset response buffer
memset(rsp, 0, CMDLEN_MAXBYTES);
// Min length is 1 byte so this should always be here
switch (cmd[0]) {
case FW_CMD_NAME_VERSION:
puts("request: name-version\n");
if (hdr.len != 1) {
// Bad length - give them an empty response
fwreply(hdr, FW_RSP_NAME_VERSION, rsp);
break;
}
memcpy(rsp, (uint8_t *)&local_name0, 4);
memcpy(rsp + 4, (uint8_t *)&local_name1, 4);
memcpy(rsp + 8, (uint8_t *)&local_ver, 4);
fwreply(hdr, FW_RSP_NAME_VERSION, rsp);
break;
case FW_CMD_LOAD_APP_SIZE:
puts("request: load-app-size\n");
if (hdr.len != 32) {
// Bad length
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
break;
}
// cmd[1..4] contains the size.
local_app_size = cmd[1] + (cmd[2] << 8) +
(cmd[3] << 16) + (cmd[4] << 24);
puts("app size: ");
putinthex(local_app_size);
lf();
if (local_app_size > APP_MAX_SIZE) {
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
break;
}
*app_size = local_app_size;
*app_addr = 0;
// Reset where to start loading the program
loadaddr = (uint8_t *)APP_RAM_ADDR;
left = *app_size;
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
break;
case FW_CMD_LOAD_APP_DATA:
puts("request: load-app-data\n");
if (hdr.len != 128 || *app_size == 0) {
// Bad length of this command or bad app size -
// they need to call FW_CMD_LOAD_APP_SIZE first
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_APP_DATA, rsp);
break;
}
if (left > 127) {
nbytes = 127;
} else {
nbytes = left;
}
memcpy(loadaddr, cmd + 1, nbytes);
loadaddr += nbytes;
left -= nbytes;
if (left == 0) {
uint8_t scratch[64];
puts("Fully loaded ");
putinthex(*app_size);
lf();
*app_addr = APP_RAM_ADDR;
// Get the Blake2S digest of the app - store it
// for later queries
blake2s(digest, 32, NULL, 0,
(const void *)*app_addr, *app_size);
print_digest(digest);
// CDI = hash(uds, hash(app))
uint32_t local_cdi[8];
// Only word aligned access to UDS
wordcpy(scratch, (void *)uds, 8);
memcpy(scratch + 32, digest, 32);
blake2s((void *)local_cdi, 32, NULL, 0,
(const void *)scratch, 64);
// Only word aligned access to CDI
wordcpy((void *)cdi, (void *)local_cdi, 8);
}
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_LOAD_APP_DATA, rsp);
break;
case FW_CMD_RUN_APP:
puts("request: run-app\n");
if (hdr.len != 1) {
// Bad length
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_RUN_APP, rsp);
break;
}
if (*app_size > 0 && *app_addr != 0) {
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_RUN_APP, rsp);
// Flip over to application mode
*switch_app = 1;
// Jump to app - doesn't return
// First clears memory of firmware remains
puts("Jumping to ");
putinthex(*app_addr);
lf();
// clang-format off
asm volatile(
"li a0, 0x40000000;" // MTA1_MKDF_RAM_BASE
"li a1, 0x40010000;"
"loop:;"
"sw zero, 0(a0);"
"addi a0, a0, 4;"
"blt a0, a1, loop;"
// Get value at MTA1_MKDF_MMIO_MTA1_APP_ADDR
"lui a0,0xff000;"
"lw a0,0x030(a0);"
"jalr x0,0(a0);"
::: "memory");
// clang-format on
}
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_RUN_APP, rsp);
break;
case FW_CMD_GET_APP_DIGEST:
puts("request: get-app-digest\n");
memcpy(rsp, &digest, 32);
fwreply(hdr, FW_RSP_GET_APP_DIGEST, rsp);
break;
default:
puts("Received unknown firmware command: 0x");
puthex(cmd[0]);
lf();
}
}
return (int)0xcafebabe;
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "proto.h"
#include "../mta1_mkdf_mem.h"
#include "lib.h"
#include "types.h"
// clang-format off
static volatile uint32_t *can_rx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_RX_STATUS;
static volatile uint32_t *rx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_RX_DATA;
static volatile uint32_t *can_tx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_TX_STATUS;
static volatile uint32_t *tx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_TX_DATA;
static volatile uint32_t *led = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_LED;
// clang-format on
uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status, enum cmdlen len)
{
return (id << 5) | (endpoint << 3) | (status << 2) | len;
}
int parseframe(uint8_t b, struct frame_header *hdr)
{
if ((b & 0x80) != 0) {
// Bad version
return -1;
}
if ((b & 0x4) != 0) {
// Must be 0
return -1;
}
hdr->id = (b & 0x60) >> 5;
hdr->endpoint = (b & 0x18) >> 3;
// Length
switch (b & 0x3) {
case LEN_1:
hdr->len = 1;
break;
case LEN_4:
hdr->len = 4;
break;
case LEN_32:
hdr->len = 32;
break;
case LEN_128:
hdr->len = 128;
break;
default:
// Unknown length
return -1;
}
return 0;
}
// Send a firmware reply with a frame header, response code rspcode and
// following data in buf
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf)
{
size_t nbytes;
enum cmdlen len; // length covering (rspcode + length of buf)
switch (rspcode) {
case FW_RSP_NAME_VERSION:
len = LEN_32;
nbytes = 32;
break;
case FW_RSP_LOAD_APP_SIZE:
len = LEN_4;
nbytes = 4;
break;
case FW_RSP_LOAD_APP_DATA:
len = LEN_4;
nbytes = 4;
break;
case FW_RSP_RUN_APP:
len = LEN_4;
nbytes = 4;
break;
case FW_RSP_GET_APP_DIGEST:
len = LEN_128;
nbytes = 128;
break;
default:
puts("fwreply(): Unknown response code: 0x");
puthex(rspcode);
lf();
return;
}
// Frame Protocol Header
writebyte(genhdr(hdr.id, hdr.endpoint, 0x0, len));
// FW protocol header
writebyte(rspcode);
nbytes--;
write(buf, nbytes);
}
void writebyte(uint8_t b)
{
for (;;) {
if (*can_tx) {
*tx = b;
return;
}
}
}
void write(uint8_t *buf, size_t nbytes)
{
for (int i = 0; i < nbytes; i++) {
writebyte(buf[i]);
}
}
uint8_t readbyte()
{
for (;;) {
if (*can_rx) {
return *rx;
}
}
}
uint8_t readbyte_ledflash(int ledvalue, int loopcount)
{
int led_on = 0;
for (;;) {
*led = led_on ? ledvalue : 0;
for (int i = 0; i < loopcount; i++) {
if (*can_rx) {
return *rx;
}
}
led_on = !led_on;
}
}
void read(uint8_t *buf, size_t nbytes)
{
for (int n = 0; n < nbytes; n++) {
buf[n] = readbyte();
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "types.h"
#ifndef PROTO_H
#define PROTO_H
enum endpoints {
DST_HW_IFPGA,
DST_HW_AFPGA,
DST_FW,
DST_SW
};
enum cmdlen {
LEN_1,
LEN_4,
LEN_32,
LEN_128
};
#define CMDLEN_MAXBYTES 128
// clang-format off
enum fwcmd {
FW_CMD_NAME_VERSION = 0x01,
FW_RSP_NAME_VERSION = 0x02,
FW_CMD_LOAD_APP_SIZE = 0x03,
FW_RSP_LOAD_APP_SIZE = 0x04,
FW_CMD_LOAD_APP_DATA = 0x05,
FW_RSP_LOAD_APP_DATA = 0x06,
FW_CMD_RUN_APP = 0x07,
FW_RSP_RUN_APP = 0x08,
FW_CMD_GET_APP_DIGEST = 0x09,
FW_RSP_GET_APP_DIGEST = 0x10
};
// clang-format on
enum status {
STATUS_OK,
STATUS_BAD
};
struct frame_header {
uint8_t id;
enum endpoints endpoint;
enum cmdlen len;
};
uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status, enum cmdlen len);
int parseframe(uint8_t b, struct frame_header *hdr);
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf);
void writebyte(uint8_t b);
void write(uint8_t *buf, size_t nbytes);
uint8_t readbyte();
uint8_t readbyte_ledflash(int ledvalue, int loopcount);
void read(uint8_t *buf, size_t nbytes);
#endif

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
.section ".text.init"
.globl _start
_start:
li x1, 0
li x2, 0
li x3, 0
li x4, 0
li x5, 0
li x6, 0
li x7, 0
li x8, 0
li x9, 0
li x10,0
li x11,0
li x12,0
li x13,0
li x14,0
li x15,0
li x16,0
li x17,0
li x18,0
li x19,0
li x20,0
li x21,0
li x22,0
li x23,0
li x24,0
li x25,0
li x26,0
li x27,0
li x28,0
li x29,0
li x30,0
li x31,0
/* init stack to right under where we load app at 0x40010000 */
li sp, 0x4000fff0
/* copy data section */
la a0, _sidata
la a1, _sdata
la a2, _edata
bge a1, a2, end_init_data
loop_init_data:
lw a3, 0(a0)
sw a3, 0(a1)
addi a0, a0, 4
addi a1, a1, 4
blt a1, a2, loop_init_data
end_init_data:
/* zero-init bss section */
la a0, _sbss
la a1, _ebss
bge a0, a1, end_init_bss
loop_init_bss:
sw zero, 0(a0)
addi a0, a0, 4
blt a0, a1, loop_init_bss
end_init_bss:
call main
loop:
j loop

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef TYPES_H
#define TYPES_H
typedef unsigned int uintptr_t;
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned long size_t;
#define NULL ((char *)0)
#endif

View File

@ -0,0 +1,109 @@
/*
* QEMU RISC-V Board Compatible with Mullvad MTA1-MKDF platform
*
* Copyright (c) 2022 Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// clang-format off
#ifndef HW_MTA1_MKDF_MEM_H
#define HW_MTA1_MKDF_MEM_H
// The canonical location of this file is:
// repo: https://github.com/tillitis/tillitis-key1
// path: /hw/application_fpga/fw/mta1_mkdf_mem.h
// The contents are derived from the Verilog code. For use by QEMU model,
// firmware, and apps.
enum {
MTA1_MKDF_ROM_BASE = 0x00000000, // 0b00000000...
MTA1_MKDF_RAM_BASE = 0x40000000, // 0b01000000...
MTA1_MKDF_RESERVED_BASE = 0x80000000, // 0b10000000...
MTA1_MKDF_MMIO_BASE = 0xc0000000, // 0b11000000...
MTA1_MKDF_MMIO_SIZE = 0xffffffff - MTA1_MKDF_MMIO_BASE,
MTA1_MKDF_MMIO_TRNG_BASE = MTA1_MKDF_MMIO_BASE | 0x00000000,
MTA1_MKDF_MMIO_TIMER_BASE = MTA1_MKDF_MMIO_BASE | 0x01000000,
MTA1_MKDF_MMIO_UDS_BASE = MTA1_MKDF_MMIO_BASE | 0x02000000,
MTA1_MKDF_MMIO_UART_BASE = MTA1_MKDF_MMIO_BASE | 0x03000000,
MTA1_MKDF_MMIO_TOUCH_BASE = MTA1_MKDF_MMIO_BASE | 0x04000000,
// This "core" only exists in QEMU
MTA1_MKDF_MMIO_QEMU_BASE = MTA1_MKDF_MMIO_BASE | 0x3e000000,
MTA1_MKDF_MMIO_MTA1_BASE = MTA1_MKDF_MMIO_BASE | 0x3f000000, // 0xff000000
MTA1_MKDF_NAME0_SUFFIX = 0x00,
MTA1_MKDF_NAME1_SUFFIX = 0x04,
MTA1_MKDF_VERSION_SUFFIX = 0x08,
MTA1_MKDF_MMIO_TRNG_NAME0 = MTA1_MKDF_MMIO_TRNG_BASE | MTA1_MKDF_NAME0_SUFFIX,
MTA1_MKDF_MMIO_TRNG_NAME1 = MTA1_MKDF_MMIO_TRNG_BASE | MTA1_MKDF_NAME1_SUFFIX,
MTA1_MKDF_MMIO_TRNG_VERSION = MTA1_MKDF_MMIO_TRNG_BASE | MTA1_MKDF_VERSION_SUFFIX,
MTA1_MKDF_MMIO_TRNG_STATUS = MTA1_MKDF_MMIO_TRNG_BASE | 0x24,
MTA1_MKDF_MMIO_TRNG_STATUS_READY_BIT = 0,
MTA1_MKDF_MMIO_TRNG_SAMPLE_RATE = MTA1_MKDF_MMIO_TRNG_BASE | 0x40,
MTA1_MKDF_MMIO_TRNG_ENTROPY = MTA1_MKDF_MMIO_TRNG_BASE | 0x80,
MTA1_MKDF_MMIO_TIMER_NAME0 = MTA1_MKDF_MMIO_TIMER_BASE | MTA1_MKDF_NAME0_SUFFIX,
MTA1_MKDF_MMIO_TIMER_NAME1 = MTA1_MKDF_MMIO_TIMER_BASE | MTA1_MKDF_NAME1_SUFFIX,
MTA1_MKDF_MMIO_TIMER_VERSION = MTA1_MKDF_MMIO_TIMER_BASE | MTA1_MKDF_VERSION_SUFFIX,
MTA1_MKDF_MMIO_TIMER_CTRL = MTA1_MKDF_MMIO_TIMER_BASE | 0x20,
MTA1_MKDF_MMIO_TIMER_CTRL_START_BIT = 0,
MTA1_MKDF_MMIO_TIMER_CTRL_STOP_BIT = 1,
MTA1_MKDF_MMIO_TIMER_STATUS = MTA1_MKDF_MMIO_TIMER_BASE | 0x24,
MTA1_MKDF_MMIO_TIMER_STATUS_READY_BIT = 0,
MTA1_MKDF_MMIO_TIMER_PRESCALER = MTA1_MKDF_MMIO_TIMER_BASE | 0x28,
MTA1_MKDF_MMIO_TIMER_TIMER = MTA1_MKDF_MMIO_TIMER_BASE | 0x2c,
MTA1_MKDF_MMIO_UDS_NAME0 = MTA1_MKDF_MMIO_UDS_BASE | MTA1_MKDF_NAME0_SUFFIX,
MTA1_MKDF_MMIO_UDS_NAME1 = MTA1_MKDF_MMIO_UDS_BASE | MTA1_MKDF_NAME1_SUFFIX,
MTA1_MKDF_MMIO_UDS_VERSION = MTA1_MKDF_MMIO_UDS_BASE | MTA1_MKDF_VERSION_SUFFIX,
MTA1_MKDF_MMIO_UDS_FIRST = MTA1_MKDF_MMIO_UDS_BASE | 0x40,
MTA1_MKDF_MMIO_UDS_LAST = MTA1_MKDF_MMIO_UDS_BASE | 0x5c, // Address of last 32-bit word of UDS
MTA1_MKDF_MMIO_UART_NAME0 = MTA1_MKDF_MMIO_UART_BASE | MTA1_MKDF_NAME0_SUFFIX,
MTA1_MKDF_MMIO_UART_NAME1 = MTA1_MKDF_MMIO_UART_BASE | MTA1_MKDF_NAME1_SUFFIX,
MTA1_MKDF_MMIO_UART_VERSION = MTA1_MKDF_MMIO_UART_BASE | MTA1_MKDF_VERSION_SUFFIX,
MTA1_MKDF_MMIO_UART_BIT_RATE = MTA1_MKDF_MMIO_UART_BASE | 0x40,
MTA1_MKDF_MMIO_UART_DATA_BITS = MTA1_MKDF_MMIO_UART_BASE | 0x44,
MTA1_MKDF_MMIO_UART_STOP_BITS = MTA1_MKDF_MMIO_UART_BASE | 0x48,
MTA1_MKDF_MMIO_UART_RX_STATUS = MTA1_MKDF_MMIO_UART_BASE | 0x80,
MTA1_MKDF_MMIO_UART_RX_DATA = MTA1_MKDF_MMIO_UART_BASE | 0x84,
MTA1_MKDF_MMIO_UART_TX_STATUS = MTA1_MKDF_MMIO_UART_BASE | 0x100,
MTA1_MKDF_MMIO_UART_TX_DATA = MTA1_MKDF_MMIO_UART_BASE | 0x104,
MTA1_MKDF_MMIO_TOUCH_NAME0 = MTA1_MKDF_MMIO_TOUCH_BASE | MTA1_MKDF_NAME0_SUFFIX,
MTA1_MKDF_MMIO_TOUCH_NAME1 = MTA1_MKDF_MMIO_TOUCH_BASE | MTA1_MKDF_NAME1_SUFFIX,
MTA1_MKDF_MMIO_TOUCH_VERSION = MTA1_MKDF_MMIO_TOUCH_BASE | MTA1_MKDF_VERSION_SUFFIX,
MTA1_MKDF_MMIO_TOUCH_STATUS = MTA1_MKDF_MMIO_TOUCH_BASE | 0x24,
MTA1_MKDF_MMIO_TOUCH_STATUS_EVENT_BIT = 0,
// TODO HW core/addr is not yet defined for this:
MTA1_MKDF_MMIO_QEMU_UDA = MTA1_MKDF_MMIO_QEMU_BASE | 0x20,
// This will only ever exist in QEMU:
MTA1_MKDF_MMIO_QEMU_DEBUG = MTA1_MKDF_MMIO_QEMU_BASE | 0x1000,
MTA1_MKDF_MMIO_MTA1_NAME0 = MTA1_MKDF_MMIO_MTA1_BASE | MTA1_MKDF_NAME0_SUFFIX,
MTA1_MKDF_MMIO_MTA1_NAME1 = MTA1_MKDF_MMIO_MTA1_BASE | MTA1_MKDF_NAME1_SUFFIX,
MTA1_MKDF_MMIO_MTA1_VERSION = MTA1_MKDF_MMIO_MTA1_BASE | MTA1_MKDF_VERSION_SUFFIX,
MTA1_MKDF_MMIO_MTA1_SWITCH_APP = MTA1_MKDF_MMIO_MTA1_BASE | 0x20,
MTA1_MKDF_MMIO_MTA1_LED = MTA1_MKDF_MMIO_MTA1_BASE | 0x24,
MTA1_MKDF_MMIO_MTA1_LED_R_BIT = 2,
MTA1_MKDF_MMIO_MTA1_LED_G_BIT = 1,
MTA1_MKDF_MMIO_MTA1_LED_B_BIT = 0,
MTA1_MKDF_MMIO_MTA1_GPIO = MTA1_MKDF_MMIO_MTA1_BASE | 0x28,
MTA1_MKDF_MMIO_MTA1_GPIO1_BIT = 0,
MTA1_MKDF_MMIO_MTA1_GPIO2_BIT = 1,
MTA1_MKDF_MMIO_MTA1_GPIO3_BIT = 2,
MTA1_MKDF_MMIO_MTA1_GPIO4_BIT = 3,
MTA1_MKDF_MMIO_MTA1_APP_ADDR = MTA1_MKDF_MMIO_MTA1_BASE | 0x30, // 0x4000_0000
MTA1_MKDF_MMIO_MTA1_APP_SIZE = MTA1_MKDF_MMIO_MTA1_BASE | 0x34,
MTA1_MKDF_MMIO_MTA1_DEBUG = MTA1_MKDF_MMIO_MTA1_BASE | 0x40,
MTA1_MKDF_MMIO_MTA1_CDI_FIRST = MTA1_MKDF_MMIO_MTA1_BASE | 0x80,
MTA1_MKDF_MMIO_MTA1_CDI_LAST = MTA1_MKDF_MMIO_MTA1_BASE | 0x9c, // Address of last 32-bit word of CDI.
MTA1_MKDF_MMIO_MTA1_UDI_FIRST = MTA1_MKDF_MMIO_MTA1_BASE | 0xc0,
MTA1_MKDF_MMIO_MTA1_UDI_LAST = MTA1_MKDF_MMIO_MTA1_BASE | 0xc4, // Address of last 32-bit word of UDI.
};
#endif

View File

@ -0,0 +1,4 @@
.PHONY: fmt
fmt:
# Uses ../.clang-format
clang-format --verbose -i main.c

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../mta1_mkdf/lib.h"
#include "../mta1_mkdf/proto.h"
#include "../mta1_mkdf/types.h"
#include "../mta1_mkdf_mem.h"
// clang-format off
volatile uint32_t *mta1name0 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME0;
volatile uint32_t *mta1name1 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME1;
volatile uint32_t *uds = (volatile uint32_t *)MTA1_MKDF_MMIO_UDS_FIRST;
volatile uint32_t *uda = (volatile uint32_t *)MTA1_MKDF_MMIO_QEMU_UDA; // Only in QEMU right now
volatile uint32_t *cdi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_CDI_FIRST;
volatile uint32_t *udi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_UDI_FIRST;
volatile uint32_t *switch_app = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_SWITCH_APP;
// clang-format on
// TODO Real UDA is 4 words (16 bytes)
#define UDA_WORDS 1
void test_puts(char *reason)
{
for (char *c = reason; *c != '\0'; c++) {
writebyte(*c);
}
}
void test_putsn(char *p, int n)
{
for (int i = 0; i < n; i++) {
writebyte(p[i]);
}
}
void test_puthex(uint8_t c)
{
unsigned int upper = (c >> 4) & 0xf;
unsigned int lower = c & 0xf;
writebyte(upper < 10 ? '0' + upper : 'a' - 10 + upper);
writebyte(lower < 10 ? '0' + lower : 'a' - 10 + lower);
}
void test_puthexn(uint8_t *p, int n)
{
for (int i = 0; i < n; i++) {
test_puthex(p[i]);
}
}
void test_reverseword(uint32_t *wordp)
{
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
((*wordp & 0x0000ff00) << 8) | ((*wordp & 0x000000ff) << 24);
}
int main()
{
uint8_t in;
// Wait for terminal program and a character to be typed
in = readbyte();
test_puts("Hello, I'm testfw on:");
// Output the MTA1 core's NAME0 and NAME1
uint32_t name;
wordcpy(&name, (void *)mta1name0, 1);
test_reverseword(&name);
test_putsn((char *)&name, 4);
test_puts(" ");
wordcpy(&name, (void *)mta1name1, 1);
test_reverseword(&name);
test_putsn((char *)&name, 4);
test_puts("\r\n");
int anyfailed = 0;
uint32_t uds_local[8];
uint32_t uds_zeros[8];
memset(uds_zeros, 0, 8 * 4);
// Should get non-empty UDS
wordcpy(uds_local, (void *)uds, 8);
if (memeq(uds_local, uds_zeros, 8 * 4)) {
test_puts("FAIL: UDS empty!\r\n");
anyfailed = 1;
}
// Should NOT be able to read from UDS again
wordcpy(uds_local, (void *)uds, 8);
if (!memeq(uds_local, uds_zeros, 8 * 4)) {
test_puts("FAIL: Could read UDS a second time!\r\n");
anyfailed = 1;
}
// TODO test UDA once we have it in real hw
// uint32_t uda_local[UDA_WORDS];
// uint32_t uda_zeros[UDA_WORDS];
// memset(uda_zeros, 0, UDA_WORDS*4);
// // Should get non-empty UDA
// wordcpy(uda_local, (void *)uda, UDA_WORDS);
// if (memeq(uda_local, uda_zeros, UDA_WORDS*4)) {
// test_puts("FAIL: UDA empty!\r\n");
// anyfailed = 1;
// }
uint32_t udi_local[2];
uint32_t udi_zeros[2];
memset(udi_zeros, 0, 2 * 4);
// Should get non-empty UDI
wordcpy(udi_local, (void *)udi, 2);
if (memeq(udi_local, udi_zeros, 2 * 4)) {
test_puts("FAIL: UDI empty!\r\n");
anyfailed = 1;
}
// Should be able to write to CDI in non-app mode.
uint32_t cdi_writetest[8] = {0xdeafbeef, 0xdeafbeef, 0xdeafbeef,
0xdeafbeef, 0xdeafbeef, 0xdeafbeef,
0xdeafbeef, 0xdeafbeef};
uint32_t cdi_readback[8];
wordcpy((void *)cdi, cdi_writetest, 8);
wordcpy(cdi_readback, (void *)cdi, 8);
if (!memeq(cdi_writetest, cdi_readback, 8 * 4)) {
test_puts("FAIL: Could not write to CDI in non-app mode!\r\n");
anyfailed = 1;
}
// Turn on application mode
*switch_app = 1;
// Should NOT be able to read from UDS in app-mode.
wordcpy(uds_local, (void *)uds, 8);
if (!memeq(uds_local, uds_zeros, 8 * 4)) {
test_puts("FAIL: Could read from UDS in app-mode!\r\n");
anyfailed = 1;
}
// TODO test UDA once we have in in real hw
// // Now we should NOT be able to read from UDA.
// wordcpy(uda_local, (void *)uda, UDA_WORDS);
// if (!memeq(uda_local, uda_zeros, UDA_WORDS*4)) {
// test_puts("FAIL: Could read from UDA in app-mode!\r\n");
// anyfailed = 1;
// }
uint32_t cdi_local[8];
uint32_t cdi_local2[8];
uint32_t cdi_zeros[8];
memset(cdi_zeros, 0, 8 * 4);
wordcpy(cdi_local, (void *)cdi, 8);
// Write to CDI should NOT have any effect in app mode.
wordcpy((void *)cdi, cdi_zeros, 8);
wordcpy(cdi_local2, (void *)cdi, 8);
if (!memeq(cdi_local, cdi_local2, 8 * 4)) {
test_puts("FAIL: Could write to CDI in app-mode!\r\n");
anyfailed = 1;
}
if (anyfailed) {
test_puts("Some test failed!\r\n");
} else {
test_puts("All tests passed.\r\n");
}
test_puts("Now echoing what you type...\r\n");
for (;;) {
in = readbyte(); // blocks
writebyte(in);
}
}

View File

@ -0,0 +1,467 @@
//======================================================================
//
// application_fpga.v
// ------------------
// Top level module of the application FPGA.
// The design exposes a UART interface to allow a host to
// send commands and receive resposes as needed load, execute and
// communicate with applications.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module application_fpga(
output wire interface_rx,
input wire interface_tx,
input wire touch_event,
input wire app_gpio1,
input wire app_gpio2,
output wire app_gpio3,
output wire app_gpio4,
output wire led_r,
output wire led_g,
output wire led_b
);
//----------------------------------------------------------------
// Local parameters
//----------------------------------------------------------------
// Top level mem area prefixes.
localparam ROM_PREFIX = 2'h0;
localparam RAM_PREFIX = 2'h1;
localparam RESERVED_PREFIX = 2'h2;
localparam MMIO_PREFIX = 2'h3;
// MMIO core sub-prefixes.
localparam TRNG_PREFIX = 6'h00;
localparam TIMER_PREFIX = 6'h01;
localparam UDS_PREFIX = 6'h02;
localparam UART_PREFIX = 6'h03;
localparam TOUCH_SENSE_PREFIX = 6'h04;
localparam MTA1_PREFIX = 6'h3f;
//----------------------------------------------------------------
// Registers, memories with associated wires.
//----------------------------------------------------------------
reg [31 : 0] muxed_rdata_reg;
reg [31 : 0] muxed_rdata_new;
reg muxed_ready_reg;
reg muxed_ready_new;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
wire clk;
wire reset_n;
wire cpu_valid;
wire [03 : 0] cpu_wstrb;
wire [31 : 0] cpu_addr;
wire [31 : 0] cpu_wdata;
/* verilator lint_off UNOPTFLAT */
reg rom_cs;
/* verilator lint_on UNOPTFLAT */
reg [11 : 0] rom_address;
wire [31 : 0] rom_read_data;
wire rom_ready;
reg ram_cs;
reg [3 : 0] ram_we;
reg [14 : 0] ram_address;
reg [31 : 0] ram_write_data;
wire [31 : 0] ram_read_data;
wire ram_ready;
/* verilator lint_off UNOPTFLAT */
reg trng_cs;
/* verilator lint_on UNOPTFLAT */
reg trng_we;
reg [7 : 0] trng_address;
reg [31 : 0] trng_write_data;
wire [31 : 0] trng_read_data;
wire trng_ready;
/* verilator lint_off UNOPTFLAT */
reg timer_cs;
/* verilator lint_on UNOPTFLAT */
reg timer_we;
reg [7 : 0] timer_address;
reg [31 : 0] timer_write_data;
wire [31 : 0] timer_read_data;
wire timer_ready;
/* verilator lint_off UNOPTFLAT */
reg uds_cs;
/* verilator lint_on UNOPTFLAT */
reg [7 : 0] uds_address;
wire [31 : 0] uds_read_data;
wire uds_ready;
/* verilator lint_off UNOPTFLAT */
reg uart_cs;
/* verilator lint_on UNOPTFLAT */
reg uart_we;
reg [7 : 0] uart_address;
reg [31 : 0] uart_write_data;
wire [31 : 0] uart_read_data;
wire uart_ready;
/* verilator lint_off UNOPTFLAT */
reg touch_sense_cs;
/* verilator lint_on UNOPTFLAT */
reg touch_sense_we;
reg [7 : 0] touch_sense_address;
wire [31 : 0] touch_sense_read_data;
wire touch_sense_ready;
/* verilator lint_off UNOPTFLAT */
reg mta1_cs;
/* verilator lint_on UNOPTFLAT */
reg mta1_we;
reg [7 : 0] mta1_address;
reg [31 : 0] mta1_write_data;
wire [31 : 0] mta1_read_data;
wire mta1_ready;
wire fw_app_mode;
//----------------------------------------------------------------
// Concurrent assignments.
//----------------------------------------------------------------
//----------------------------------------------------------------
// Module instantiations.
//----------------------------------------------------------------
// Use the FPGA internal High Frequency OSCillator as clock source.
// 00: 48MHz, 01: 24MHz, 10: 12MHz, 11: 6MHz
SB_HFOSC #(.CLKHF_DIV("0b10")
) u_hfosc (.CLKHFPU(1'b1),.CLKHFEN(1'b1),.CLKHF(clk));
reset_gen #(.RESET_CYCLES(200))
reset_gen_inst(.clk(clk), .rst_n(reset_n));
picorv32 #(
.ENABLE_COUNTERS(0),
.LATCHED_MEM_RDATA(0),
.TWO_STAGE_SHIFT(0),
.TWO_CYCLE_ALU(0),
.CATCH_MISALIGN(0),
.CATCH_ILLINSN(0),
.COMPRESSED_ISA(1),
.ENABLE_FAST_MUL(1),
.ENABLE_DIV(0),
.BARREL_SHIFTER(1)
) cpu(
.clk(clk),
.resetn(reset_n),
.mem_valid(cpu_valid),
.mem_ready(muxed_ready_reg),
.mem_addr (cpu_addr),
.mem_wdata(cpu_wdata),
.mem_wstrb(cpu_wstrb),
.mem_rdata(muxed_rdata_reg),
// Defined unsed ports. Makes lint happy,
// but still needs to help lint with empty ports.
/* verilator lint_off PINCONNECTEMPTY */
.irq(32'h0),
.eoi(),
.trap(),
.trace_valid(),
.trace_data(),
.mem_instr(),
.mem_la_read(),
.mem_la_write(),
.mem_la_addr(),
.mem_la_wdata(),
.mem_la_wstrb(),
.pcpi_valid(),
.pcpi_insn(),
.pcpi_rs1(),
.pcpi_rs2(),
.pcpi_wr(1'h0),
.pcpi_rd(32'h0),
.pcpi_wait(1'h0),
.pcpi_ready(1'h0)
/* verilator lint_on PINCONNECTEMPTY */
);
rom rom_inst(
.clk(clk),
.reset_n(reset_n),
.cs(rom_cs),
.address(rom_address),
.read_data(rom_read_data),
.ready(rom_ready)
);
ram ram_inst(
.clk(clk),
.reset_n(reset_n),
.cs(ram_cs),
.we(ram_we),
.address(ram_address),
.write_data(ram_write_data),
.read_data(ram_read_data),
.ready(ram_ready)
);
figaro trng_inst(
.clk(clk),
.reset_n(reset_n),
.cs(trng_cs),
.we(trng_we),
.address(trng_address),
.write_data(trng_write_data),
.read_data(trng_read_data),
.ready(trng_ready)
);
timer timer_inst(
.clk(clk),
.reset_n(reset_n),
.cs(timer_cs),
.we(timer_we),
.address(timer_address),
.write_data(timer_write_data),
.read_data(timer_read_data),
.ready(timer_ready)
);
uds uds_inst(
.clk(clk),
.reset_n(reset_n),
.fw_app_mode(fw_app_mode),
.cs(uds_cs),
.address(uds_address),
.read_data(uds_read_data),
.ready(uds_ready)
);
uart uart_inst(
.clk(clk),
.reset_n(reset_n),
.rxd(interface_tx),
.txd(interface_rx),
.cs(uart_cs),
.we(uart_we),
.address(uart_address),
.write_data(uart_write_data),
.read_data(uart_read_data),
.ready(uart_ready)
);
touch_sense touch_sense_inst(
.clk(clk),
.reset_n(reset_n),
.touch_event(touch_event),
.cs(touch_sense_cs),
.we(touch_sense_we),
.address(touch_sense_address),
.read_data(touch_sense_read_data),
.ready(touch_sense_ready)
);
mta1 mta1_inst(
.clk(clk),
.reset_n(reset_n),
.fw_app_mode(fw_app_mode),
.led_r(led_r),
.led_g(led_g),
.led_b(led_b),
.gpio1(app_gpio1),
.gpio2(app_gpio2),
.gpio3(app_gpio3),
.gpio4(app_gpio4),
.cs(mta1_cs),
.we(mta1_we),
.address(mta1_address),
.write_data(mta1_write_data),
.read_data(mta1_read_data),
.ready(mta1_ready)
);
//----------------------------------------------------------------
// Reg_update.
// Posedge triggered with synchronous, active low reset.
//----------------------------------------------------------------
always @(posedge clk)
begin : reg_update
if (!reset_n) begin
muxed_rdata_reg <= 32'h0;
muxed_ready_reg <= 1'h0;
end
else begin
muxed_rdata_reg <= muxed_rdata_new;
muxed_ready_reg <= muxed_ready_new;
end
end
//----------------------------------------------------------------
// cpu_mem_ctrl
// CPU memory decode and control logic.
//----------------------------------------------------------------
always @*
begin : cpu_mem_ctrl
reg [1 : 0] area_prefix;
reg [5 : 0] core_prefix;
area_prefix = cpu_addr[31 : 30];
core_prefix = cpu_addr[29 : 24];
muxed_ready_new = 1'h0;
muxed_rdata_new = 32'h0;
rom_cs = 1'h0;
rom_address = cpu_addr[13 : 2];
ram_cs = 1'h0;
ram_we = cpu_wstrb;
ram_address = cpu_addr[16 : 2];
ram_write_data = cpu_wdata;
trng_cs = 1'h0;
trng_we = |cpu_wstrb;
trng_address = cpu_addr[10 : 2];
trng_write_data = cpu_wdata;
timer_cs = 1'h0;
timer_we = |cpu_wstrb;
timer_address = cpu_addr[10 : 2];
timer_write_data = cpu_wdata;
uds_cs = 1'h0;
uds_address = cpu_addr[10 : 2];
uart_cs = 1'h0;
uart_we = |cpu_wstrb;
uart_address = cpu_addr[10 : 2];
uart_write_data = cpu_wdata;
touch_sense_cs = 1'h0;
touch_sense_we = |cpu_wstrb;
touch_sense_address = cpu_addr[10 : 2];
mta1_cs = 1'h0;
mta1_we = |cpu_wstrb;
mta1_address = cpu_addr[10 : 2];
mta1_write_data = cpu_wdata;
if (cpu_valid && !muxed_ready_reg) begin
case (area_prefix)
ROM_PREFIX: begin
rom_cs = 1'h1;
muxed_rdata_new = rom_read_data;
muxed_ready_new = rom_ready;
end
RAM_PREFIX: begin
ram_cs = 1'h1;
muxed_rdata_new = ram_read_data;
muxed_ready_new = ram_ready;
end
RESERVED_PREFIX: begin
muxed_rdata_new = 32'h0;
muxed_ready_new = 1'h1;
end
MMIO_PREFIX: begin
case (core_prefix)
TRNG_PREFIX: begin
trng_cs = 1'h1;
muxed_rdata_new = trng_read_data;
muxed_ready_new = trng_ready;
end
TIMER_PREFIX: begin
timer_cs = 1'h1;
muxed_rdata_new = timer_read_data;
muxed_ready_new = timer_ready;
end
UDS_PREFIX: begin
uds_cs = 1'h1;
muxed_rdata_new = uds_read_data;
muxed_ready_new = uds_ready;
end
UART_PREFIX: begin
uart_cs = 1'h1;
muxed_rdata_new = uart_read_data;
muxed_ready_new = uart_ready;
end
TOUCH_SENSE_PREFIX: begin
touch_sense_cs = 1'h1;
muxed_rdata_new = touch_sense_read_data;
muxed_ready_new = touch_sense_ready;
end
MTA1_PREFIX: begin
mta1_cs = 1'h1;
muxed_rdata_new = mta1_read_data;
muxed_ready_new = mta1_ready;
end
default: begin
muxed_rdata_new = 32'h0;
muxed_ready_new = 1'h1;
end
endcase // case (core_prefix)
end // case: MMIO_PREFIX
default: begin
muxed_rdata_new = 32'h0;
muxed_ready_new = 1'h1;
end
endcase // case (area_prefix)
end
end
endmodule // application_fpga
//======================================================================
// EOF application_fpga.v
//======================================================================

View File

@ -0,0 +1,144 @@
//======================================================================
//
// ram.v
// -----
// Module that encapsulates the four SPRAM blocks in the Lattice
// iCE40UP 5K device. This creates a single 32-bit wide,
// 128 kByte large memory.
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module ram(
input wire clk,
input wire reset_n,
input wire cs,
input wire [03 : 0] we,
input wire [14 : 0] address,
input wire [31 : 0] write_data,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Registers and wires.
//----------------------------------------------------------------
reg ready_reg;
reg cs0;
reg cs1;
reg [31 : 0] read_data0;
reg [31 : 0] read_data1;
reg [31 : 0] muxed_read_data;
//----------------------------------------------------------------
// Concurrent assignment of ports.
//----------------------------------------------------------------
assign read_data = muxed_read_data;
assign ready = ready_reg;
//----------------------------------------------------------------
// SPRAM instances.
//----------------------------------------------------------------
SB_SPRAM256KA spram0(
.ADDRESS(address[13:0]),
.DATAIN(write_data[15:0]),
.MASKWREN({we[1], we[1], we[0], we[0]}),
.WREN(we[1] | we[0]),
.CHIPSELECT(cs0),
.CLOCK(clk),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1),
.DATAOUT(read_data0[15:0])
);
SB_SPRAM256KA spram1(
.ADDRESS(address[13:0]),
.DATAIN(write_data[31:16]),
.MASKWREN({we[3], we[3], we[2], we[2]}),
.WREN(we[3] | we[2]),
.CHIPSELECT(cs0),
.CLOCK(clk),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1),
.DATAOUT(read_data0[31:16])
);
SB_SPRAM256KA spram2(
.ADDRESS(address[13:0]),
.DATAIN(write_data[15:0]),
.MASKWREN({we[1], we[1], we[0], we[0]}),
.WREN(we[1] | we[0]),
.CHIPSELECT(cs1),
.CLOCK(clk),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1),
.DATAOUT(read_data1[15:0])
);
SB_SPRAM256KA spram3(
.ADDRESS(address[13:0]),
.DATAIN(write_data[31:16]),
.MASKWREN({we[3], we[3], we[2], we[2]}),
.WREN(we[3] | we[2]),
.CHIPSELECT(cs1),
.CLOCK(clk),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1),
.DATAOUT(read_data1[31:16])
);
//----------------------------------------------------------------
// reg_update
//
// Posedge triggered with synchronous, active low reset.
// This simply creates a one cycle access latency to match
// the latency of the spram blocks.
//----------------------------------------------------------------
always @(posedge clk)
begin : reg_update
if (!reset_n) begin
ready_reg <= 1'h0;
end
else begin
ready_reg <= cs;
end
end
//----------------------------------------------------------------
// mem_mux
//----------------------------------------------------------------
always @*
begin : mem_mux
cs0 = 1'h0;
cs1 = 1'h0;
if (address[14]) begin
cs1 = cs;
muxed_read_data = read_data1;
end else begin
cs0 = cs;
muxed_read_data = read_data0;
end
end
endmodule // ram
//======================================================================
// EOF ram.v
//======================================================================

View File

@ -0,0 +1,72 @@
//======================================================================
//
// reset_gen.v
// -----------
// Reset generator for iCE40 based systems.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module reset_gen #(parameter RESET_CYCLES = 200)
(
input wire clk,
output wire rst_n
);
//----------------------------------------------------------------
// Registers with associated wires.
//----------------------------------------------------------------
reg [7 : 0] rst_ctr_reg = 8'h0;
reg [7 : 0] rst_ctr_new;
reg rst_ctr_we;
reg rst_n_reg = 1'h0;
reg rst_n_new;
//----------------------------------------------------------------
// Concurrent assignment.
//----------------------------------------------------------------
assign rst_n = rst_n_reg;
//----------------------------------------------------------------
// reg_update.
//----------------------------------------------------------------
always @(posedge clk)
begin : reg_update
rst_n_reg <= rst_n_new;
if (rst_ctr_we)
rst_ctr_reg <= rst_ctr_new;
end
//----------------------------------------------------------------
// rst_logic.
//----------------------------------------------------------------
always @*
begin : rst_logic
rst_n_new = 1'h1;
rst_ctr_new = 8'h0;
rst_ctr_we = 1'h0;
if (rst_ctr_reg < RESET_CYCLES) begin
rst_n_new = 1'h0;
rst_ctr_new = rst_ctr_reg + 1'h1;
rst_ctr_we = 1'h1;
end
end
endmodule // reset_gen
//======================================================================
// EOF reset_gen.v
//======================================================================

View File

@ -0,0 +1,67 @@
//======================================================================
//
// rom..v
// ------
// Firmware ROM module. Implemented using Embedded Block RAM
// in the FPGA.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module rom(
input wire clk,
input wire reset_n,
input wire cs,
input wire [11 : 0] address,
output wire [31 : 0] read_data,
output wire ready
);
//----------------------------------------------------------------
// Registers, memories with associated wires.
//----------------------------------------------------------------
// Size of the sysMem Embedded Block RAM (EBR) memory primarily
// used for code storage (ROM). The size is number of
// 32-bit words. Each EBR is 4kbit in size, and (at most)
// 16-bit wide. Thus means that we use pairs of EBRs, and
// each pair store 256 32bit words.
// The size of the EBR allocated to memory must match the
// size of the firmware file generated by the Makefile.
localparam EBR_MEM_SIZE = `BRAM_FW_SIZE;
reg [31 : 0] memory [0 : (EBR_MEM_SIZE - 1)];
initial $readmemh(`FIRMWARE_HEX, memory);
reg [31 : 0] rom_rdata;
reg rom_ready;
//----------------------------------------------------------------
// Concurrent assignments of ports.
//----------------------------------------------------------------
assign read_data = rom_rdata;
assign ready = rom_ready;
//----------------------------------------------------------------
// rom_logic
//----------------------------------------------------------------
always @*
begin : rom_logic
rom_rdata = memory[address];
rom_ready = cs;
end
endmodule // rom
//======================================================================
// EOF rom..v
//======================================================================

View File

@ -0,0 +1,92 @@
//======================================================================
//
// spram.v
// -------
// Module that encapsulates two of the SPRAM blocks in the Lattice
// iCE40UP 5K device. This creates a single 32-bit wide,
// 64 kByte large memory.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
module spram(
input wire clk,
input wire rst_n,
input wire cs,
input wire [03 : 0] wen,
input wire [13 : 0] addr,
input wire [31 : 0] wdata,
output wire ready,
output wire [31 : 0] rdata
);
//----------------------------------------------------------------
// Registers and wires.
//----------------------------------------------------------------
reg ready_reg;
reg ready_new;
//----------------------------------------------------------------
//----------------------------------------------------------------
assign ready = ready_reg;
//----------------------------------------------------------------
// SPRAM instances.
//----------------------------------------------------------------
SB_SPRAM256KA spram0(
.ADDRESS(addr[13:0]),
.DATAIN(wdata[15:0]),
.MASKWREN({wen[1], wen[1], wen[0], wen[0]}),
.WREN(wen[1]|wen[0]),
.CHIPSELECT(cs),
.CLOCK(clk),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1),
.DATAOUT(rdata[15:0])
);
SB_SPRAM256KA spram1(
.ADDRESS(addr[13:0]),
.DATAIN(wdata[31:16]),
.MASKWREN({wen[3], wen[3], wen[2], wen[2]}),
.WREN(wen[3]|wen[2]),
.CHIPSELECT(cs),
.CLOCK(clk),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1),
.DATAOUT(rdata[31:16])
);
//----------------------------------------------------------------
// reg_update.
//
// Posedge triggered with synchronous, active low reset.
// This simply creates a one cycle access delay to allow the
// memory access to complete.
//----------------------------------------------------------------
always @(posedge clk)
begin : reg_update
if (!rst_n) begin
ready_reg <= 1'h0;
end
else begin
ready_reg <= cs;
end
end
endmodule // spram
//======================================================================
// EOF spram.v
//======================================================================

View File

@ -0,0 +1,342 @@
//======================================================================
//
// application_fpga_verilator.cc
// -----------------------------
// Wrapper to allow simulation of the application_fpga using Verilator.
//
//
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
#include <pty.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include "Vapplication_fpga.h"
#include "verilated.h"
// Joachim says:
// Clock: 12 MHz, 38400 bps
// Divisor = 12*10E6 / 38400 = 312
#define BIT_DIV 312
struct uart {
int bit_div;
unsigned int ts;
unsigned int tx_next_ts;
unsigned int rx_next_ts;
int rx_state; /* 0, (idle), 1 (start), 2..9 (data), 10 (stop), 11 (stop end) */
int tx_state; /* 0, (idle), 1..8 (data), 9 (stop), 10 (stop end) */
uint8_t tx_data;
uint8_t rx_data;
int rx_has_data;
int tx_has_data;
uint8_t *tx;
uint8_t *rx;
};
void uart_init(struct uart *u, uint8_t *tx, uint8_t *rx, int bit_div);
void uart_tick(struct uart *u);
int uart_can_send(struct uart *u);
int uart_send(struct uart *u, uint8_t data);
int uart_recv(struct uart *u, uint8_t *data);
void uart_init(struct uart *u, uint8_t *tx, uint8_t *rx, int bit_div)
{
u->bit_div = bit_div;
u->ts = 0;
u->tx_next_ts = 0;
u->rx_next_ts = 0;
u->rx_state = 0;
u->tx_state = 0;
u->tx_data = 0;
u->rx_data = 0;
u->rx_has_data = 0;
u->tx_has_data = 0;
u->tx = tx;
*u->tx = 1;
u->rx = rx;
}
void uart_rx_tick(struct uart *u)
{
if (u->rx_state == 0) {
// Idle
if (!u->rx_has_data && !*u->rx) { // Active low
u->rx_next_ts = u->ts + u->bit_div / 2; // sample mid-point
u->rx_state = 1;
}
return;
}
if (u->rx_state == 1) {
// Start
if (u->ts < u->rx_next_ts)
return;
if (*u->rx) {
u->rx_state = 0; // Back to idle, shouldn't happen
return;
}
u->rx_next_ts += u->bit_div;
u->rx_data = 0;
u->rx_state = 2;
return;
}
if (u->rx_state > 1 && u->rx_state < 10) {
// Data
if (u->ts < u->rx_next_ts)
return;
u->rx_next_ts += u->bit_div;
u->rx_data |= (!!*u->rx) << (u->rx_state - 2);
u->rx_state++;
return;
}
if (u->rx_state == 10) {
// Stop
if (u->ts < u->rx_next_ts)
return;
if (!*u->rx) {
u->rx_state = 0; // Back to dle, shouldn't happen
return;
}
u->rx_next_ts += u->bit_div/2;
u->rx_has_data = 1;
u->rx_state = 11;
return;
}
if (u->rx_state == 11) {
if (u->ts < u->rx_next_ts)
return;
u->rx_state = 0;
return;
}
}
int uart_recv(struct uart *u, uint8_t *data)
{
if (u->rx_has_data && (u->rx_state == 0 || u->rx_state == 11)) {
*data = u->rx_data;
u->rx_has_data = 0;
return 1;
}
return 0;
}
void uart_tx_tick(struct uart *u)
{
if (u->tx_state == 0) {
// Idle
if (u->tx_has_data) {
u->tx_next_ts = u->ts + u->bit_div;
*u->tx = 0; // Start
u->tx_state = 1;
}
return;
}
if (u->tx_state > 0 && u->tx_state < 9) {
// Data
if (u->ts < u->tx_next_ts)
return;
u->tx_next_ts += u->bit_div;
*u->tx = (u->tx_data >> (u->tx_state - 1)) & 1;
u->tx_state++;
return;
}
if (u->tx_state == 9) {
// Stop
if (u->ts < u->tx_next_ts)
return;
u->tx_next_ts += u->bit_div;
*u->tx = 1; // Stop
u->tx_has_data = 0;
u->tx_state = 10;
return;
}
if (u->tx_state == 10) {
if (u->ts < u->tx_next_ts)
return;
u->tx_state = 0;
return;
}
}
int uart_can_send(struct uart *u)
{
return !u->tx_has_data;
}
int uart_send(struct uart *u, uint8_t data)
{
if (!uart_can_send(u))
return 0;
u->tx_has_data = 1;
u->tx_data = data;
return 1;
}
void uart_tick(struct uart *u)
{
u->ts++;
uart_rx_tick(u);
uart_tx_tick(u);
}
struct pty {
int amaster;
int aslave;
char slave[32];
};
int pty_init(struct pty *p);
int pty_can_recv(struct pty *p);
int pty_recv(struct pty *p, uint8_t *data);
void pty_send(struct pty *p, uint8_t data);
int pty_init(struct pty *p)
{
struct termios tty;
int flags;
memset(p, 0, sizeof(*p));
if (openpty(&p->amaster, &p->aslave, p->slave, NULL, NULL) < 0)
return -1;
if (tcgetattr(p->aslave, &tty) < 0)
return -1;
cfmakeraw(&tty);
if (tcsetattr(p->aslave, TCSAFLUSH, &tty) < 0)
return -1;
if ((flags = fcntl(p->amaster, F_GETFL, 0) < 0))
return -1;
flags |= O_NONBLOCK;
if (fcntl(p->amaster, F_SETFL, flags) < 0)
return -1;
printf("pty: %s\n", p->slave);
return 0;
}
int pty_can_recv(struct pty *p)
{
struct pollfd fds = {p->amaster, POLLIN, 0};
char c;
return poll(&fds, 1, 0) == 1;
}
int pty_recv(struct pty *p, uint8_t *data)
{
return read(p->amaster, data, 1) == 1;
}
void pty_send(struct pty *p, uint8_t data)
{
ssize_t i __attribute__((unused));
i = write(p->amaster, &data, 1);
}
volatile int touch_cyc = 0;
void sighandler(int)
{
touch_cyc = 1000;
}
void touch(uint8_t *touch_event)
{
if (touch_cyc > 0) {
touch_cyc--;
*touch_event = 1;
} else {
*touch_event = 0;
}
}
vluint64_t main_time = 0;
double sc_time_stamp()
{
return main_time;
}
int main(int argc, char **argv, char **env)
{
Verilated::commandArgs(argc, argv);
int r = 0, g = 0, b = 0;
Vapplication_fpga top;
struct uart u;
struct pty p;
int err;
if (signal(SIGUSR1, sighandler) == SIG_ERR)
return -1;
printf("generate touch event: \"$ kill -USR1 %d\"\n", (int)getpid());
err = pty_init(&p);
if (err)
return -1;
uart_init(&u, &top.interface_tx, &top.interface_rx, BIT_DIV);
top.clk = 0;
while (!Verilated::gotFinish()) {
uint8_t to_host = 0;
top.clk = !top.clk;
if (main_time < 10)
goto skip;
if (!top.clk) {
touch(&top.touch_event);
uart_tick(&u);
}
if (pty_can_recv(&p) && uart_can_send(&u)) {
uint8_t from_host = 0;
pty_recv(&p, &from_host);
uart_send(&u, from_host);
}
if (uart_recv(&u, &to_host) == 1) {
pty_send(&p, to_host);
}
skip:
main_time++;
top.eval();
}
}

View File

@ -0,0 +1,482 @@
//======================================================================
//
// application_fpga.v
// ------------------
// Top level module of the application FPGA.
// The design exposes a UART interface to allow a host to
// send commands and receive resposes as needed load, execute and
// communicate with applications.
//
//
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
//`define VERBOSE
`ifdef VERBOSE
`define verbose(debug_command) debug_command
`else
`define verbose(debug_command)
`endif
module application_fpga(
input wire clk,
output wire valid,
output wire [03 : 0] wstrb,
output wire [31 : 0] addr,
output wire [31 : 0] wdata,
output wire [31 : 0] rdata,
output wire ready,
output wire interface_rx,
input wire interface_tx,
input wire touch_event,
input wire app_gpio1,
input wire app_gpio2,
output wire app_gpio3,
output wire app_gpio4,
output wire led_r,
output wire led_g,
output wire led_b
);
//----------------------------------------------------------------
// Local parameters
//----------------------------------------------------------------
// Top level mem area prefixes.
localparam ROM_PREFIX = 2'h0;
localparam RAM_PREFIX = 2'h1;
localparam RESERVED_PREFIX = 2'h2;
localparam MMIO_PREFIX = 2'h3;
// MMIO core mem sub-prefixes.
localparam TRNG_PREFIX = 6'h00;
localparam TIMER_PREFIX = 6'h01;
localparam UDS_PREFIX = 6'h02;
localparam UART_PREFIX = 6'h03;
localparam TOUCH_SENSE_PREFIX = 6'h04;
localparam MTA1_PREFIX = 6'h3f;
//----------------------------------------------------------------
// Registers, memories with associated wires.
//----------------------------------------------------------------
reg [31 : 0] muxed_rdata_reg;
reg [31 : 0] muxed_rdata_new;
reg muxed_ready_reg;
reg muxed_ready_new;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
wire reset_n;
wire cpu_valid;
wire [03 : 0] cpu_wstrb;
wire [31 : 0] cpu_addr;
wire [31 : 0] cpu_wdata;
/* verilator lint_off UNOPTFLAT */
reg rom_cs;
/* verilator lint_on UNOPTFLAT */
reg [11 : 0] rom_address;
wire [31 : 0] rom_read_data;
wire rom_ready;
reg ram_cs;
reg [3 : 0] ram_we;
reg [14 : 0] ram_address;
reg [31 : 0] ram_write_data;
wire [31 : 0] ram_read_data;
wire ram_ready;
/* verilator lint_off UNOPTFLAT */
reg trng_cs;
/* verilator lint_on UNOPTFLAT */
reg trng_we;
reg [7 : 0] trng_address;
reg [31 : 0] trng_write_data;
wire [31 : 0] trng_read_data;
wire trng_ready;
/* verilator lint_off UNOPTFLAT */
reg timer_cs;
/* verilator lint_on UNOPTFLAT */
reg timer_we;
reg [7 : 0] timer_address;
reg [31 : 0] timer_write_data;
wire [31 : 0] timer_read_data;
wire timer_ready;
/* verilator lint_off UNOPTFLAT */
reg uds_cs;
/* verilator lint_on UNOPTFLAT */
reg [7 : 0] uds_address;
wire [31 : 0] uds_read_data;
wire uds_ready;
/* verilator lint_off UNOPTFLAT */
reg uart_cs;
/* verilator lint_on UNOPTFLAT */
reg uart_we;
reg [7 : 0] uart_address;
reg [31 : 0] uart_write_data;
wire [31 : 0] uart_read_data;
wire uart_ready;
/* verilator lint_off UNOPTFLAT */
reg touch_sense_cs;
/* verilator lint_on UNOPTFLAT */
reg touch_sense_we;
reg [7 : 0] touch_sense_address;
wire [31 : 0] touch_sense_read_data;
wire touch_sense_ready;
/* verilator lint_off UNOPTFLAT */
reg mta1_cs;
/* verilator lint_on UNOPTFLAT */
reg mta1_we;
reg [7 : 0] mta1_address;
reg [31 : 0] mta1_write_data;
wire [31 : 0] mta1_read_data;
wire mta1_ready;
wire fw_app_mode;
//----------------------------------------------------------------
// Concurrent assignments.
//----------------------------------------------------------------
assign valid = cpu_valid;
assign wstrb = cpu_wstrb;
assign addr = cpu_addr;
assign wdata = cpu_wdata;
assign rdata = muxed_rdata_reg;
assign ready = muxed_ready_reg;
//----------------------------------------------------------------
// Module instantiations.
//----------------------------------------------------------------
reset_gen #(.RESET_CYCLES(200))
reset_gen_inst(.clk(clk), .rst_n(reset_n));
picorv32 #(
.ENABLE_COUNTERS(0),
.LATCHED_MEM_RDATA(0),
.TWO_STAGE_SHIFT(0),
.TWO_CYCLE_ALU(0),
.CATCH_MISALIGN(0),
.CATCH_ILLINSN(0),
.COMPRESSED_ISA(1),
.ENABLE_MUL(1),
.ENABLE_DIV(0),
.BARREL_SHIFTER(0)
) cpu(
.clk(clk),
.resetn(reset_n),
.mem_valid(cpu_valid),
.mem_addr (cpu_addr),
.mem_wdata(cpu_wdata),
.mem_wstrb(cpu_wstrb),
.mem_rdata(muxed_rdata_reg),
.mem_ready(muxed_ready_reg),
// Defined unsed ports. Makes lint happy,
// but still needs to help lint with empty ports.
/* verilator lint_off PINCONNECTEMPTY */
.irq(32'h0),
.eoi(),
.trap(),
.trace_valid(),
.trace_data(),
.mem_instr(),
.mem_la_read(),
.mem_la_write(),
.mem_la_addr(),
.mem_la_wdata(),
.mem_la_wstrb(),
.pcpi_valid(),
.pcpi_insn(),
.pcpi_rs1(),
.pcpi_rs2(),
.pcpi_wr(1'h0),
.pcpi_rd(32'h0),
.pcpi_wait(1'h0),
.pcpi_ready(1'h0)
/* verilator lint_on PINCONNECTEMPTY */
);
rom rom_inst(
.clk(clk),
.reset_n(reset_n),
.cs(rom_cs),
.address(rom_address),
.read_data(rom_read_data),
.ready(rom_ready)
);
ram ram_inst(
.clk(clk),
.reset_n(reset_n),
.cs(ram_cs),
.we(ram_we),
.address(ram_address),
.write_data(ram_write_data),
.read_data(ram_read_data),
.ready(ram_ready)
);
timer timer_inst(
.clk(clk),
.reset_n(reset_n),
.cs(timer_cs),
.we(timer_we),
.address(timer_address),
.write_data(timer_write_data),
.read_data(timer_read_data),
.ready(timer_ready)
);
uds uds_inst(
.clk(clk),
.reset_n(reset_n),
.cs(uds_cs),
.address(uds_address),
.read_data(uds_read_data),
.ready(uds_ready)
);
uart uart_inst(
.clk(clk),
.reset_n(reset_n),
.rxd(interface_tx),
.txd(interface_rx),
.cs(uart_cs),
.we(uart_we),
.address(uart_address),
.write_data(uart_write_data),
.read_data(uart_read_data),
.ready(uart_ready)
);
touch_sense touch_sense_inst(
.clk(clk),
.reset_n(reset_n),
.touch_event(touch_event),
.cs(touch_sense_cs),
.we(touch_sense_we),
.address(touch_sense_address),
.read_data(touch_sense_read_data),
.ready(touch_sense_ready)
);
mta1 mta1_inst(
.clk(clk),
.reset_n(reset_n),
.fw_app_mode(fw_app_mode),
.led_r(led_r),
.led_g(led_g),
.led_b(led_b),
.gpio1(app_gpio1),
.gpio2(app_gpio2),
.gpio3(app_gpio3),
.gpio4(app_gpio4),
.cs(mta1_cs),
.we(mta1_we),
.address(mta1_address),
.write_data(mta1_write_data),
.read_data(mta1_read_data),
.ready(mta1_ready)
);
//----------------------------------------------------------------
// Reg_update.
// Posedge triggered with synchronous, active low reset.
//----------------------------------------------------------------
always @(posedge clk)
begin : reg_update
if (!reset_n) begin
muxed_ready_reg <= 1'h0;
muxed_rdata_reg <= 32'h0;
end
else begin
muxed_ready_reg <= muxed_ready_new;
muxed_rdata_reg <= muxed_rdata_new;
end
end
//----------------------------------------------------------------
// cpu_mem_ctrl
// CPU memory decode and control logic.
//----------------------------------------------------------------
always @*
begin : cpu_mem_ctrl
reg [1 : 0] area_prefix;
reg [5 : 0] core_prefix;
area_prefix = cpu_addr[31 : 30];
core_prefix = cpu_addr[29 : 24];
muxed_ready_new = 1'h0;
muxed_rdata_new = 32'h0;
rom_cs = 1'h0;
rom_address = cpu_addr[13 : 2];
ram_cs = 1'h0;
ram_we = cpu_wstrb;
ram_address = cpu_addr[16 : 2];
ram_write_data = cpu_wdata;
trng_cs = 1'h0;
trng_we = |cpu_wstrb;
trng_address = cpu_addr[10 : 2];
trng_write_data = cpu_wdata;
timer_cs = 1'h0;
timer_we = |cpu_wstrb;
timer_address = cpu_addr[10 : 2];
timer_write_data = cpu_wdata;
uds_cs = 1'h0;
uds_address = cpu_addr[10 : 2];
uart_cs = 1'h0;
uart_we = |cpu_wstrb;
uart_address = cpu_addr[10 : 2];
uart_write_data = cpu_wdata;
touch_sense_cs = 1'h0;
touch_sense_we = |cpu_wstrb;
touch_sense_address = cpu_addr[10 : 2];
mta1_cs = 1'h0;
mta1_we = |cpu_wstrb;
mta1_address = cpu_addr[10 : 2];
mta1_write_data = cpu_wdata;
if (cpu_valid && !muxed_ready_reg) begin
case (area_prefix)
ROM_PREFIX: begin
`verbose($display("Access to ROM area");)
rom_cs = 1'h1;
muxed_rdata_new = rom_read_data;
muxed_ready_new = rom_ready;
end
RAM_PREFIX: begin
`verbose($display("Access to RAM area");)
ram_cs = 1'h1;
muxed_rdata_new = ram_read_data;
muxed_ready_new = ram_ready;
end
RESERVED_PREFIX: begin
`verbose($display("Access to RESERVED area");)
muxed_rdata_new = 32'h00000000;
muxed_ready_new = 1'h1;
end
MMIO_PREFIX: begin
`verbose($display("Access to MMIO area");)
case (core_prefix)
TRNG_PREFIX: begin
`verbose($display("Access to TRNG core");)
trng_cs = 1'h1;
muxed_rdata_new = trng_read_data;
muxed_ready_new = trng_ready;
end
TIMER_PREFIX: begin
`verbose($display("Access to TIMER core");)
timer_cs = 1'h1;
muxed_rdata_new = timer_read_data;
muxed_ready_new = timer_ready;
end
UDS_PREFIX: begin
`verbose($display("Access to UDS core");)
uds_cs = 1'h1;
muxed_rdata_new = uds_read_data;
muxed_ready_new = uds_ready;
end
UART_PREFIX: begin
`verbose($display("Access to UART core");)
uart_cs = 1'h1;
muxed_rdata_new = uart_read_data;
muxed_ready_new = uart_ready;
end
TOUCH_SENSE_PREFIX: begin
`verbose($display("Access to TOUCH_SENSE core");)
touch_sense_cs = 1'h1;
muxed_rdata_new = touch_sense_read_data;
muxed_ready_new = touch_sense_ready;
end
MTA1_PREFIX: begin
`verbose($display("Access to MTA1 core");)
mta1_cs = 1'h1;
muxed_rdata_new = mta1_read_data;
muxed_ready_new = mta1_ready;
end
default: begin
`verbose($display("UNDEFINED MMIO");)
muxed_rdata_new = 32'h00000000;
muxed_ready_new = 1'h1;
end
endcase // case (core_prefix)
end // case: MMIO_PREFIX
default: begin
`verbose($display("UNDEFINED AREA");)
muxed_rdata_new = 32'h0;
muxed_ready_new = 1'h1;
end
endcase // case (area_prefix)
end
end
endmodule // application_fpga
//======================================================================
// EOF application_fpga.v
//======================================================================

View File

@ -0,0 +1,26 @@
#!/usr/bin/env python3
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
from sys import argv
binfile = argv[1]
nwords = int(argv[2])
with open(binfile, "rb") as f:
bindata = f.read()
assert len(bindata) < 4*nwords
assert len(bindata) % 4 == 0
for i in range(nwords):
if i < len(bindata) // 4:
w = bindata[4*i : 4*i+4]
print("%02x%02x%02x%02x" % (w[3], w[2], w[1], w[0]))
else:
print("0")

View File

@ -0,0 +1,8 @@
# Tillitis Key Provisioning Tool
## Introduction
Tillis Key Provisioning Tool (tpt) is a program for generating the 32 byte Unique Device Secret (UDS). The tool will also generate the 8 byte Unique Device Identity. Both the UDS and the UDI are injected into the FPGA bitstream file during build.
The UDS is generated using HKDF (RFC 5869), and the user is expected to supply a secret as part of the input to the HKDF Extract operation. The Input Keying Material is generated by extracting 256 bytes using the Python secrets module.
The tool uses [python-hkdf](https://github.com/casebeer/python-hkdf).

View File

@ -0,0 +1,95 @@
# Copyright (c) 2012 Christopher H. Casebeer. All rights reserved.
# SPDX-License-Identifier: BSD-2
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import division
import hmac
import hashlib
import sys
if sys.version_info[0] == 3:
buffer = lambda x: x
def hkdf_extract(salt, input_key_material, hash=hashlib.sha512):
'''
Extract a pseudorandom key suitable for use with hkdf_expand
from the input_key_material and a salt using HMAC with the
provided hash (default SHA-512).
salt should be a random, application-specific byte string. If
salt is None or the empty string, an all-zeros string of the same
length as the hash's block size will be used instead per the RFC.
See the HKDF draft RFC and paper for usage notes.
'''
hash_len = hash().digest_size
if salt == None or len(salt) == 0:
salt = bytearray((0,) * hash_len)
return hmac.new(bytes(salt), buffer(input_key_material), hash).digest()
def hkdf_expand(pseudo_random_key, info=b"", length=32, hash=hashlib.sha512):
'''
Expand `pseudo_random_key` and `info` into a key of length `bytes` using
HKDF's expand function based on HMAC with the provided hash (default
SHA-512). See the HKDF draft RFC and paper for usage notes.
'''
hash_len = hash().digest_size
length = int(length)
if length > 255 * hash_len:
raise Exception("Cannot expand to more than 255 * %d = %d bytes using the specified hash function" %\
(hash_len, 255 * hash_len))
blocks_needed = length // hash_len + (0 if length % hash_len == 0 else 1) # ceil
okm = b""
output_block = b""
for counter in range(blocks_needed):
output_block = hmac.new(pseudo_random_key, buffer(output_block + info + bytearray((counter + 1,))),\
hash).digest()
okm += output_block
return okm[:length]
class Hkdf(object):
'''
Wrapper class for HKDF extract and expand functions
'''
def __init__(self, salt, input_key_material, hash=hashlib.sha256):
'''
Extract a pseudorandom key from `salt` and `input_key_material` arguments.
See the HKDF draft RFC for guidance on setting these values. The constructor
optionally takes a `hash` arugment defining the hash function use,
defaulting to hashlib.sha256.
'''
self._hash = hash
self._prk = hkdf_extract(salt, input_key_material, self._hash)
def expand(self, info=b"", length=32):
'''
Generate output key material based on an `info` value
Arguments:
- info - context to generate the OKM
- length - length in bytes of the key to generate
See the HKDF draft RFC for guidance.
'''
return hkdf_expand(self._prk, info, length, self._hash)

View File

@ -0,0 +1,129 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#=======================================================================
#
# tpt.py
# ------
# TillitisKey Provisioning Tool.
#
# The tool use HKDF (RFC5869) to generate the UDS.
#
# Copyright (C) 2022 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#=======================================================================
import sys
import argparse
from secrets import token_bytes
from binascii import unhexlify
from hkdf import Hkdf
#-------------------------------------------------------------------
#-------------------------------------------------------------------
def gen_uds(verbose, arg_uss):
if verbose:
print("\nGenerating UDS")
if arg_uss == None:
uss = str.encode(input("Enter user supplied secret: "))
else:
uss = str.encode(arg_uss)
ikm = token_bytes(256)
my_hkdf = Hkdf(uss, ikm)
uds = my_hkdf.expand(b"TillitisKey UDS", 32)
uds_hex = uds.hex()
if verbose:
print("\nGenerated UDS:")
print(uds_hex, "\n")
return uds_hex
#-------------------------------------------------------------------
#-------------------------------------------------------------------
def save_uds(verbose, uds):
if verbose:
print("Writing uds.hex")
with open ("uds.hex", 'w', encoding = 'utf-8') as uds_file:
for i in range(8):
uds_file.write(uds[(i*8) : (i*8 + 8)]+"\n")
#-------------------------------------------------------------------
#-------------------------------------------------------------------
def gen_udi(verbose, pid, vid, rev, serial):
if verbose:
print("Generating UDI")
if vid == None:
vid = int(input("Enter Vendor ID (0 - 65535): "))
if pid == None:
pid = int(input("Enter Product ID (0 - 255): "))
if rev == None:
rev = int(input("Enter revision (0 - 15): "))
if serial == None:
serial = int(input("Enter serial number (0 - (2**32 -1)): "))
assert vid < 65536
assert pid < 256
assert rev < 16
assert serial < 2**32
udi_hex = ("0%04x%02x%1x%08x" % (vid, pid, rev, serial))
if verbose:
print("\nGenerated UDI:")
print(udi_hex, "\n")
return udi_hex
#-------------------------------------------------------------------
#-------------------------------------------------------------------
def save_udi(verbose, udi):
if verbose:
print("Writing udi.hex")
with open ("udi.hex", 'w', encoding = 'utf-8') as udi_file:
udi_file.write(udi[0 : 8] + "\n")
udi_file.write(udi[8 : 16] + "\n")
def enc_str(b):
return bytestring.decode(sys.getfilesystemencoding())
#-------------------------------------------------------------------
#-------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="Verbose operation", action="store_true")
parser.add_argument("--uss", help="User supplied secret", type=str)
parser.add_argument("--vid", help="Vendor id (0 - 65535)", type=int)
parser.add_argument("--pid", help="Product id (0 - 2555", type=int)
parser.add_argument("--rev", help="Revision number (0 - 15)", type=int)
parser.add_argument("--serial", help="Serial number (0 - (2**31 - 1))", type=int)
args = parser.parse_args()
print(args)
if args.verbose:
print("TillitisKey Provisining Tool (TPT)")
uds = gen_uds(args.verbose, args.uss)
save_uds(args.verbose, uds)
udi = gen_udi(args.verbose, args.pid, args.vid, args.rev, args.serial)
save_udi(args.verbose, udi)
#-------------------------------------------------------------------
#-------------------------------------------------------------------
if __name__=="__main__":
sys.exit(main())

View File

@ -0,0 +1,83 @@
# Install instructions
## 1. Copy the files locally
Copy the `RP-Pico Libraries` folder wherever you like on your computer.
## 2. Install the Raspberry Pi Pico board schema symbol
Use the `KiCad | Preferences | Manage Symbol Libraries...` command to manage the symbol library:
![symbol library manager](Images/Image07.png)
then select the global tab and click on the folder button:
![symbol library manager](Images/Image08.png)
navigate to the `RP-Pico Libraries` folder, select the `MCU_RaspberryPi_and_Boards.kicad_sym` file and open it:
![symbol library manager](Images/Image09.png)
et voilà, the first step is completed:
![symbol library manager](Images/Image10.png)
You can now close the symbol libraries manager window.
## 3. Install the Raspberry Pi Pico board footprint
You can use a similar approach to add the footprint to the footprint libraries manager, but I've found some issues that I've solved using the footprint editor, so here are the steps I suggest you to follow:
Open the footprint editor
![symbol library manager](Images/Image11.png)
wait for the footprints to load... then use the `File | Add Library` command:
![symbol library manager](Images/Image12.png)
confirm the `Global` choice:
![symbol library manager](Images/Image13.png)
and select the `MCU_RaspberriPi_and_Boards.pretty` folder (yes, the folder represent a footprint library on KiCad):
![symbol library manager](Images/Image14.png)
Now the library is installed on KiCad with the Raspberry Pi Pico footprint (double click on it to see it on the editor pane):
![symbol library manager](Images/Image15.png)
Don't close the windows as the next step start from here.
## 4. Install the Raspberry Pi Pico board footprint 3D visual
If not already open, open the the footprint editor
![symbol library manager](Images/Image11.png)
double click on the `RPi_Pico_SMD_TH` footprint from the `MCU_RaspberriPi_and_Boards` library and then click on the `Footprint properties` icon:
![symbol library manager](Images/Image16.png)
In the footprint properties window, first select the `3D Settings` tab. Please note that the preview shows only the PCB board with the footprint added on step 3, without any 3D representation of the Raspberry Pi Pico board. Now click on the folder icon to add the 3D model:
![symbol library manager](Images/Image17.png)
Navigate to the `RP-Pico Libraries` folder, select the `Pico.wrl` file and wait until the model is shown in the right panel, then confirm with OK:
![symbol library manager](Images/Image18.png)
The model is already scaled and translated to match the footprint:
![symbol library manager](Images/Image19.png)
now close the `Footprint Properties` window, and the `Footprint Editor`, obviously saving the changes.
## Conclusion
Now that you've installed the schema and footprint and added the 3D model to the footprint, you can use the Raspberry Pi Pico board on your KiCad projects.
I've also added a test KiCad Project on the `Test` folder, that you can use to see an example of it.
Have fun!

View File

@ -0,0 +1,27 @@
### TPCWare KiCad Library License
The TPCWare KiCad Library is licensed under the [Creative Commons CC-BY-SA 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/legalcode), with the following exception:
---------
_To the extent that the creation of electronic designs that use 'Licensed Material' can be considered to be 'Adapted Material', then the copyright holder waives article 3 of the license with respect to these designs and any generated files which use data provided as part of the 'Licensed Material'._
---------
**What does this mean?**
TPCWare KiCad Library is licensed in such a way to ensure free use of library data for commercial, closed, and non-commercial projects without restriction. TPCWare Corporation does not wish to exert any control over designs produced using the library data, or force users to reveal proprietary information contained in their designs. Neither do we wish to force users to attribute the TPCWare KiCad Library _within their design_.
Use of the library data in a project does not (by itself) require that the design or any files generated from the design are licensed under the CC-BY-SA 4.0 License. You are free to use the library data in your own projects without the obligation to share your project files under this or any other license agreement.
However, if you wish to redistribute the TPCWare KiCad Library, or parts thereof (including in modified form) as a collection then the exception above does not apply. Redistributed library collections must be shared under the same license agreement. Under these circumstances, the libraries must also retain attribution information, including the license documents which are distributed with the library files.
----------------------
**Disclaimer of Warranties and Limitation of Liability.**
a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.

View File

@ -0,0 +1,44 @@
# KiCad-RP-Pico
A simple repository of files needed to add a 3D footprint of the Raspberry Pi Pico board to KiCad.
![Rasperry Pi Pico mechanical specification](Images/Image01.png)
## Schematic and footprint
I've started from the schematics and footprint files available on the [HeadBoffin RP_Silicon_KiCad GitHub project](https://github.com/HeadBoffin/RP_Silicon_KiCad). Because there are some errors in the readme file on how and where we can get those files from the Raspberry Pi web site, I've copied the needed files here, on the `RP-Pico Libraries` folder. Follow the [install instructions](Install%20instructions.md) to add the Raspberry Pi Pico board schema and footprint to a KiCad project:
![schema and footprint screenshot](Images/Image02.png)
## 3D Visual
Elas, with the files found in the [HeadBoffin project](https://github.com/HeadBoffin/RP_Silicon_KiCad) no visual footprint is available, as we can see on the 3D viewer:
![footprint without 3D visual screenshot](Images/Image03.png)
So I decided to create one, using the mechanical specification from the [Raspberry Pi Pico datasheet](https://datasheets.raspberrypi.org/pico/pico-datasheet.pdf):
![Rasperry Pi Pico mechanical specification](Images/Image04.png)
I've used SketchUp to create the 3D model:
![Rasperry Pi Pico mechanical specification](Images/Image05.png)
and I used the native Sketchup export function (no plugin needed) to create the VRLM file `Pico.wrl` that I've also added an the `RP-Pico Libraries` folder, as the VRLM format is one of the 3D model format that KiCad allow to use for the visual representation of a CPB footprint.
After adding it to the Raspberry Pi Pico KiCad footprint library, we can finally see the visual representation of the Raspberry Pi Pico board:
![Rasperry Pi Pico mechanical specification](Images/Image06.png)
Please note that the castellated pins of the Pico board allow it to be usable as a surface mount module. That's way you see the board simply placed on top of the PCB board.
## Install 3D visual
Please note that adding the visual file to the footprint make the use of KiCad very slow when showing the 3D viewer for the first time. On my old laptop it takes 30s to show, but only 3s to reopen it. The good news are that you can disable it while working at your project and enable it when you want a nice 3D representation of your work.
To install and enable/disable it follow the [install instructions](Install%20instructions.md).
## Test project
I've created a test project where you can find a simple usage example. The first image of this readme shows the result.
## License
Please read the [License](LICENSE) where is stated that this work is free to use.
A tweet with an image of your Raspberry Pi Pico project with a link this project and me (@tpcware) would be greatly appreciated.

View File

@ -0,0 +1,201 @@
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
(symbol "Pico" (in_bom yes) (on_board yes)
(property "Reference" "U" (id 0) (at -13.97 27.94 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "Pico" (id 1) (at 0 19.05 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "RPi_Pico:RPi_Pico_SMD_TH" (id 2) (at 0 0 90)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "" (id 3) (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "Pico_0_0"
(text "Raspberry Pi Pico" (at 0 21.59 0)
(effects (font (size 1.27 1.27)))
)
)
(symbol "Pico_0_1"
(rectangle (start -15.24 26.67) (end 15.24 -26.67)
(stroke (width 0) (type default) (color 0 0 0 0))
(fill (type background))
)
)
(symbol "Pico_1_1"
(pin bidirectional line (at -17.78 24.13 0) (length 2.54)
(name "GPIO0" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 1.27 0) (length 2.54)
(name "GPIO7" (effects (font (size 1.27 1.27))))
(number "10" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -1.27 0) (length 2.54)
(name "GPIO8" (effects (font (size 1.27 1.27))))
(number "11" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -3.81 0) (length 2.54)
(name "GPIO9" (effects (font (size 1.27 1.27))))
(number "12" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -17.78 -6.35 0) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "13" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -8.89 0) (length 2.54)
(name "GPIO10" (effects (font (size 1.27 1.27))))
(number "14" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -11.43 0) (length 2.54)
(name "GPIO11" (effects (font (size 1.27 1.27))))
(number "15" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -13.97 0) (length 2.54)
(name "GPIO12" (effects (font (size 1.27 1.27))))
(number "16" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -16.51 0) (length 2.54)
(name "GPIO13" (effects (font (size 1.27 1.27))))
(number "17" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -17.78 -19.05 0) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "18" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -21.59 0) (length 2.54)
(name "GPIO14" (effects (font (size 1.27 1.27))))
(number "19" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 21.59 0) (length 2.54)
(name "GPIO1" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 -24.13 0) (length 2.54)
(name "GPIO15" (effects (font (size 1.27 1.27))))
(number "20" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -24.13 180) (length 2.54)
(name "GPIO16" (effects (font (size 1.27 1.27))))
(number "21" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -21.59 180) (length 2.54)
(name "GPIO17" (effects (font (size 1.27 1.27))))
(number "22" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 -19.05 180) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "23" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -16.51 180) (length 2.54)
(name "GPIO18" (effects (font (size 1.27 1.27))))
(number "24" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -13.97 180) (length 2.54)
(name "GPIO19" (effects (font (size 1.27 1.27))))
(number "25" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -11.43 180) (length 2.54)
(name "GPIO20" (effects (font (size 1.27 1.27))))
(number "26" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -8.89 180) (length 2.54)
(name "GPIO21" (effects (font (size 1.27 1.27))))
(number "27" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 -6.35 180) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "28" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 -3.81 180) (length 2.54)
(name "GPIO22" (effects (font (size 1.27 1.27))))
(number "29" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -17.78 19.05 0) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
(pin input line (at 17.78 -1.27 180) (length 2.54)
(name "RUN" (effects (font (size 1.27 1.27))))
(number "30" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 1.27 180) (length 2.54)
(name "GPIO26_ADC0" (effects (font (size 1.27 1.27))))
(number "31" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 3.81 180) (length 2.54)
(name "GPIO27_ADC1" (effects (font (size 1.27 1.27))))
(number "32" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 6.35 180) (length 2.54)
(name "AGND" (effects (font (size 1.27 1.27))))
(number "33" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 8.89 180) (length 2.54)
(name "GPIO28_ADC2" (effects (font (size 1.27 1.27))))
(number "34" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 11.43 180) (length 2.54)
(name "ADC_VREF" (effects (font (size 1.27 1.27))))
(number "35" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 13.97 180) (length 2.54)
(name "3V3" (effects (font (size 1.27 1.27))))
(number "36" (effects (font (size 1.27 1.27))))
)
(pin input line (at 17.78 16.51 180) (length 2.54)
(name "3V3_EN" (effects (font (size 1.27 1.27))))
(number "37" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 17.78 19.05 180) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "38" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 21.59 180) (length 2.54)
(name "VSYS" (effects (font (size 1.27 1.27))))
(number "39" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 16.51 0) (length 2.54)
(name "GPIO2" (effects (font (size 1.27 1.27))))
(number "4" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 17.78 24.13 180) (length 2.54)
(name "VBUS" (effects (font (size 1.27 1.27))))
(number "40" (effects (font (size 1.27 1.27))))
)
(pin input line (at -2.54 -29.21 90) (length 2.54)
(name "SWCLK" (effects (font (size 1.27 1.27))))
(number "41" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 0 -29.21 90) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "42" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 2.54 -29.21 90) (length 2.54)
(name "SWDIO" (effects (font (size 1.27 1.27))))
(number "43" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 13.97 0) (length 2.54)
(name "GPIO3" (effects (font (size 1.27 1.27))))
(number "5" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 11.43 0) (length 2.54)
(name "GPIO4" (effects (font (size 1.27 1.27))))
(number "6" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 8.89 0) (length 2.54)
(name "GPIO5" (effects (font (size 1.27 1.27))))
(number "7" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -17.78 6.35 0) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "8" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -17.78 3.81 0) (length 2.54)
(name "GPIO6" (effects (font (size 1.27 1.27))))
(number "9" (effects (font (size 1.27 1.27))))
)
)
)
)

View File

@ -0,0 +1,153 @@
(footprint "RPi_Pico_SMD" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 6224DF39)
(descr "Through hole straight pin header, 2x20, 2.54mm pitch, double rows")
(tags "Through hole pin header THT 2x20 2.54mm double row")
(attr through_hole)
(fp_text reference "REF**" (at 0 0) (layer "F.SilkS")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 96315415-cfed-47d2-b3dd-d782358bd0df)
)
(fp_text value "RPi_Pico_SMD" (at 0 2.159) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 46cbe85d-ff47-428e-b187-4ebd50a66e0c)
)
(fp_text user "Copper Keepouts shown on Dwgs layer" (at 0.1 -30.2) (layer "Cmts.User")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 66ca01b3-51ff-4294-9b77-4492e98f6aec)
)
(fp_text user "${REFERENCE}" (at 0 0 180) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 83184391-76ed-44f0-8cd0-01f89f157bdb)
)
(fp_line (start 10.5 4.9) (end 10.5 5.3) (layer "F.SilkS") (width 0.12) (tstamp 099473f1-6598-46ff-a50f-4c520832170d))
(fp_line (start -10.5 -23.1) (end -10.5 -22.7) (layer "F.SilkS") (width 0.12) (tstamp 0c5dddf1-38df-43d2-b49c-e7b691dab0ab))
(fp_line (start -10.5 -20.5) (end -10.5 -20.1) (layer "F.SilkS") (width 0.12) (tstamp 0ce1dd44-f307-4f98-9f0d-478fd87daa64))
(fp_line (start 10.5 10) (end 10.5 10.4) (layer "F.SilkS") (width 0.12) (tstamp 15699041-ed40-45ee-87d8-f5e206a88536))
(fp_line (start -3.7 25.5) (end -10.5 25.5) (layer "F.SilkS") (width 0.12) (tstamp 1855ca44-ab48-4b76-a210-97fc81d916c4))
(fp_line (start 10.5 -0.2) (end 10.5 0.2) (layer "F.SilkS") (width 0.12) (tstamp 1876c30c-72b2-4a8d-9f32-bf8b213530b4))
(fp_line (start 10.5 22.7) (end 10.5 23.1) (layer "F.SilkS") (width 0.12) (tstamp 199124ca-dd64-45cf-a063-97cc545cbea7))
(fp_line (start 10.5 -23.1) (end 10.5 -22.7) (layer "F.SilkS") (width 0.12) (tstamp 1bd80cf9-f42a-4aee-a408-9dbf4e81e625))
(fp_line (start -7.493 -22.833) (end -7.493 -25.5) (layer "F.SilkS") (width 0.12) (tstamp 254f7cc6-cee1-44ca-9afe-939b318201aa))
(fp_line (start 10.5 -5.3) (end 10.5 -4.9) (layer "F.SilkS") (width 0.12) (tstamp 26a22c19-4cc5-4237-9651-0edc4f854154))
(fp_line (start -10.5 -25.5) (end 10.5 -25.5) (layer "F.SilkS") (width 0.12) (tstamp 3457afc5-3e4f-4220-81d1-b079f653a722))
(fp_line (start -10.5 20.1) (end -10.5 20.5) (layer "F.SilkS") (width 0.12) (tstamp 3b65c51e-c243-447e-bee9-832d94c1630e))
(fp_line (start -10.5 -2.7) (end -10.5 -2.3) (layer "F.SilkS") (width 0.12) (tstamp 3bbbbb7d-391c-4fee-ac81-3c47878edc38))
(fp_line (start -10.5 22.7) (end -10.5 23.1) (layer "F.SilkS") (width 0.12) (tstamp 402c62e6-8d8e-473a-a0cf-2b86e4908cd7))
(fp_line (start -10.5 -15.4) (end -10.5 -15) (layer "F.SilkS") (width 0.12) (tstamp 4970ec6e-3725-4619-b57d-dc2c2cb86ed0))
(fp_line (start -10.5 -5.3) (end -10.5 -4.9) (layer "F.SilkS") (width 0.12) (tstamp 4a53fa56-d65b-42a4-a4be-8f49c4c015bb))
(fp_line (start 10.5 -2.7) (end 10.5 -2.3) (layer "F.SilkS") (width 0.12) (tstamp 4bbde53d-6894-4e18-9480-84a6a26d5f6b))
(fp_line (start 10.5 25.5) (end 3.7 25.5) (layer "F.SilkS") (width 0.12) (tstamp 54ed3ee1-891b-418e-ab9c-6a18747d7388))
(fp_line (start 10.5 -15.4) (end 10.5 -15) (layer "F.SilkS") (width 0.12) (tstamp 57f248a7-365e-4c42-b80d-5a7d1f9dfaf3))
(fp_line (start -10.5 7.4) (end -10.5 7.8) (layer "F.SilkS") (width 0.12) (tstamp 5bab6a37-1fdf-4cf8-b571-44c962ed86e9))
(fp_line (start -10.5 -22.833) (end -7.493 -22.833) (layer "F.SilkS") (width 0.12) (tstamp 5f48b0f2-82cf-40ce-afac-440f97643c36))
(fp_line (start -10.5 -7.8) (end -10.5 -7.4) (layer "F.SilkS") (width 0.12) (tstamp 6150c02b-beb5-4af1-951e-3666a285a6ea))
(fp_line (start -10.5 4.9) (end -10.5 5.3) (layer "F.SilkS") (width 0.12) (tstamp 706c1cb9-5d96-4282-9efc-6147f0125147))
(fp_line (start -1.5 25.5) (end -1.1 25.5) (layer "F.SilkS") (width 0.12) (tstamp 749d9ed0-2ff2-4b55-abc5-f7231ec3aa28))
(fp_line (start -10.5 -12.9) (end -10.5 -12.5) (layer "F.SilkS") (width 0.12) (tstamp 755f94aa-38f0-4a64-a7c7-6c71cb18cddf))
(fp_line (start 10.5 -20.5) (end 10.5 -20.1) (layer "F.SilkS") (width 0.12) (tstamp 80095e91-6317-4cfb-9aea-884c9a1accc5))
(fp_line (start -10.5 15.1) (end -10.5 15.5) (layer "F.SilkS") (width 0.12) (tstamp 88deea08-baa5-4041-beb7-01c299cf00e6))
(fp_line (start 1.1 25.5) (end 1.5 25.5) (layer "F.SilkS") (width 0.12) (tstamp 8a8c373f-9bc3-4cf7-8f41-4802da916698))
(fp_line (start 10.5 -12.9) (end 10.5 -12.5) (layer "F.SilkS") (width 0.12) (tstamp 9112ddd5-10d5-48b8-954f-f1d5adcacbd9))
(fp_line (start -10.5 10) (end -10.5 10.4) (layer "F.SilkS") (width 0.12) (tstamp 92f063a3-7cce-4a96-8a3a-cf5767f700c6))
(fp_line (start 10.5 2.3) (end 10.5 2.7) (layer "F.SilkS") (width 0.12) (tstamp 968a6172-7a4e-40ab-a78a-e4d03671e136))
(fp_line (start -10.5 -10.4) (end -10.5 -10) (layer "F.SilkS") (width 0.12) (tstamp 9c2999b2-1cf1-4204-9d23-243401b77aa3))
(fp_line (start -10.5 -0.2) (end -10.5 0.2) (layer "F.SilkS") (width 0.12) (tstamp 9ed09117-33cf-45a3-85a7-2606522feaf8))
(fp_line (start -10.5 17.6) (end -10.5 18) (layer "F.SilkS") (width 0.12) (tstamp a177c3b4-b04c-490e-b3fe-d3d4d7aa24a7))
(fp_line (start -10.5 12.5) (end -10.5 12.9) (layer "F.SilkS") (width 0.12) (tstamp ad4d05f5-6957-42f8-b65c-c657b9a26485))
(fp_line (start 10.5 7.4) (end 10.5 7.8) (layer "F.SilkS") (width 0.12) (tstamp af76ce95-feca-41fb-bf31-edaa26d6766a))
(fp_line (start 10.5 -10.4) (end 10.5 -10) (layer "F.SilkS") (width 0.12) (tstamp c1b11207-7c0a-49b3-a41d-2fe677d5f3b8))
(fp_line (start 10.5 17.6) (end 10.5 18) (layer "F.SilkS") (width 0.12) (tstamp c346b00c-b5e0-4939-beb4-7f48172ef334))
(fp_line (start 10.5 -7.8) (end 10.5 -7.4) (layer "F.SilkS") (width 0.12) (tstamp c3d5daf8-d359-42b2-a7c2-0d080ba7e212))
(fp_line (start -10.5 -25.5) (end -10.5 -25.2) (layer "F.SilkS") (width 0.12) (tstamp ca56e1ad-54bf-4df5-a4f7-99f5d61d0de9))
(fp_line (start 10.5 20.1) (end 10.5 20.5) (layer "F.SilkS") (width 0.12) (tstamp ca9b74ce-0dee-401c-9544-f599f4cf538d))
(fp_line (start 10.5 12.5) (end 10.5 12.9) (layer "F.SilkS") (width 0.12) (tstamp d3dd7cdb-b730-487d-804d-99150ba318ef))
(fp_line (start 10.5 -18) (end 10.5 -17.6) (layer "F.SilkS") (width 0.12) (tstamp e11ae5a5-aa10-4f10-b346-f16e33c7899a))
(fp_line (start -10.5 2.3) (end -10.5 2.7) (layer "F.SilkS") (width 0.12) (tstamp eb391a95-1c1d-4613-b508-c76b8bc13a73))
(fp_line (start 10.5 -25.5) (end 10.5 -25.2) (layer "F.SilkS") (width 0.12) (tstamp f23ac723-a36d-491d-9473-7ec0ffed332d))
(fp_line (start -10.5 -18) (end -10.5 -17.6) (layer "F.SilkS") (width 0.12) (tstamp f8b47531-6c06-4e54-9fc9-cd9d0f3dd69f))
(fp_line (start 10.5 15.1) (end 10.5 15.5) (layer "F.SilkS") (width 0.12) (tstamp fd60415a-f01a-46c5-9369-ea970e435e5b))
(fp_poly (pts
(xy -1.5 -16.5)
(xy -3.5 -16.5)
(xy -3.5 -18.5)
(xy -1.5 -18.5)
) (layer "Dwgs.User") (width 0.1) (fill solid) (tstamp 022502e0-e724-4b75-bc35-3c5984dbeb76))
(fp_poly (pts
(xy -1.5 -11.5)
(xy -3.5 -11.5)
(xy -3.5 -13.5)
(xy -1.5 -13.5)
) (layer "Dwgs.User") (width 0.1) (fill solid) (tstamp 9f969b13-1795-4747-8326-93bdc304ed56))
(fp_poly (pts
(xy 3.7 -20.2)
(xy -3.7 -20.2)
(xy -3.7 -24.9)
(xy 3.7 -24.9)
) (layer "Dwgs.User") (width 0.1) (fill solid) (tstamp b9d4de74-d246-495d-8b63-12ab2133d6d6))
(fp_poly (pts
(xy -1.5 -14)
(xy -3.5 -14)
(xy -3.5 -16)
(xy -1.5 -16)
) (layer "Dwgs.User") (width 0.1) (fill solid) (tstamp d655bb0a-cbf9-4908-ad60-7024ff468fbd))
(fp_line (start 11 26) (end -11 26) (layer "F.CrtYd") (width 0.12) (tstamp 5e755161-24a5-4650-a6e3-9836bf074412))
(fp_line (start -11 -26) (end 11 -26) (layer "F.CrtYd") (width 0.12) (tstamp 9208ea78-8dde-4b3d-91e9-5755ab5efd9a))
(fp_line (start -10.5 -24.2) (end -9.2 -25.5) (layer "F.Fab") (width 0.12) (tstamp 1bf7d0f9-0dcf-4d7c-b58c-318e3dc42bc9))
(fp_line (start 10.5 -25.5) (end 10.5 25.5) (layer "F.Fab") (width 0.12) (tstamp 247ebffd-2cb6-4379-ba6e-21861fea3913))
(fp_line (start 10.5 25.5) (end -10.5 25.5) (layer "F.Fab") (width 0.12) (tstamp 94d24676-7ae3-483c-8bd6-88d31adf00b4))
(fp_line (start -10.5 -25.5) (end 10.5 -25.5) (layer "F.Fab") (width 0.12) (tstamp 966ee9ec-860e-45bb-af89-30bda72b2032))
(fp_line (start -10.5 25.5) (end -10.5 -25.5) (layer "F.Fab") (width 0.12) (tstamp e45aa7d8-0254-4176-afd9-766820762e19))
(pad "" np_thru_hole oval (at 2.425 -20.97) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask) (tstamp 1861450d-e718-4eee-89be-56d3334bef29))
(pad "" np_thru_hole oval (at -2.425 -20.97) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask) (tstamp 1d257dac-277d-4b80-9286-dd0e04ef5f8d))
(pad "" np_thru_hole oval (at 2.725 -24) (size 1.8 1.8) (drill 1.8) (layers *.Cu *.Mask) (tstamp 372f7e30-c773-4278-b448-9703d0bbb68a))
(pad "" np_thru_hole oval (at -2.725 -24) (size 1.8 1.8) (drill 1.8) (layers F&B.Cu *.Mask) (tstamp baa6854b-78fb-4bdc-8754-979b449b6762))
(pad "1" smd rect (at -8.89 -24.13) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 761c8e29-382a-475c-a37a-7201cc9cd0f5))
(pad "2" smd rect (at -8.89 -21.59) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp e50c80c5-80c4-46a3-8c1e-c9c3a71a0934))
(pad "3" smd rect (at -8.89 -19.05) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 7233cb6b-d8fd-4fcd-9b4f-8b0ed19b1b12))
(pad "4" smd rect (at -8.89 -16.51) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp df83f395-2d18-47e2-a370-952ca41c2b3a))
(pad "5" smd rect (at -8.89 -13.97) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 653a86ba-a1ae-4175-9d4c-c788087956d0))
(pad "6" smd rect (at -8.89 -11.43) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 3ed2c840-383d-4cbd-bc3b-c4ea4c97b333))
(pad "7" smd rect (at -8.89 -8.89) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 6a0919c2-460c-4229-b872-14e318e1ba8b))
(pad "8" smd rect (at -8.89 -6.35) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp d1c19c11-0a13-4237-b6b4-fb2ef1db7c6d))
(pad "9" smd rect (at -8.89 -3.81) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 29cbb0bc-f66b-4d11-80e7-5bb270e42496))
(pad "10" smd rect (at -8.89 -1.27) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp c401e9c6-1deb-4979-99be-7c801c952098))
(pad "11" smd rect (at -8.89 1.27) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 355ced6c-c08a-4586-9a09-7a9c624536f6))
(pad "12" smd rect (at -8.89 3.81) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp c2dd13db-24b6-40f1-b75b-b9ab893d92ea))
(pad "13" smd rect (at -8.89 6.35) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp d8200a86-aa75-47a3-ad2a-7f4c9c999a6f))
(pad "14" smd rect (at -8.89 8.89) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 465137b4-f6f7-4d51-9b40-b161947d5cc1))
(pad "15" smd rect (at -8.89 11.43) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp d1cd5391-31d2-459f-8adb-4ae3f304a833))
(pad "16" smd rect (at -8.89 13.97) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 4086cbd7-6ba7-4e63-8da9-17e60627ee17))
(pad "17" smd rect (at -8.89 16.51) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp bb8162f0-99c8-4884-be5b-c0d0c7e81ff6))
(pad "18" smd rect (at -8.89 19.05) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 91fc5800-6029-46b1-848d-ca0091f97267))
(pad "19" smd rect (at -8.89 21.59) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 275b6416-db29-42cc-9307-bf426917c3b4))
(pad "20" smd rect (at -8.89 24.13) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 3c22d605-7855-4cc6-8ad2-906cadbd02dc))
(pad "21" smd rect (at 8.89 24.13) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 24adc223-60f0-4497-98a3-d664c5a13280))
(pad "22" smd rect (at 8.89 21.59) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 13ac70df-e9b9-44e5-96e6-20f0b0dc6a3a))
(pad "23" smd rect (at 8.89 19.05) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 278a91dc-d57d-4a5c-a045-34b6bd84131f))
(pad "24" smd rect (at 8.89 16.51) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 98966de3-2364-43d8-a2e0-b03bb9487b03))
(pad "25" smd rect (at 8.89 13.97) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 4cc0e615-05a0-4f42-a208-4011ba8ef841))
(pad "26" smd rect (at 8.89 11.43) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 4641c87c-bffa-41fe-ae77-be3a97a6f797))
(pad "27" smd rect (at 8.89 8.89) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp da546d77-4b03-4562-8fc6-837fd68e7691))
(pad "28" smd rect (at 8.89 6.35) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp e2fac877-439c-4da0-af2e-5fdc70f85d42))
(pad "29" smd rect (at 8.89 3.81) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 2ea8fa6f-efc3-40fe-bcf9-05bfa46ead4f))
(pad "30" smd rect (at 8.89 1.27) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 9da1ace0-4181-4f12-80f8-16786a9e5c07))
(pad "31" smd rect (at 8.89 -1.27) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 29126f72-63f7-4275-8b12-6b96a71c6f17))
(pad "32" smd rect (at 8.89 -3.81) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp af186015-d283-4209-aade-a247e5de01df))
(pad "33" smd rect (at 8.89 -6.35) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 8d063f79-9282-4820-bcf4-1ff3c006cf08))
(pad "34" smd rect (at 8.89 -8.89) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 0554bea0-89b2-4e25-9ea3-4c73921c94cb))
(pad "35" smd rect (at 8.89 -11.43) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 88606262-3ac5-44a1-aacc-18b26cf4d396))
(pad "36" smd rect (at 8.89 -13.97) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp cd1cff81-9d8a-4511-96d6-4ddb79484001))
(pad "37" smd rect (at 8.89 -16.51) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 22962957-1efd-404d-83db-5b233b6c15b0))
(pad "38" smd rect (at 8.89 -19.05) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 8eb98c56-17e4-4de6-a3e3-06dcfa392040))
(pad "39" smd rect (at 8.89 -21.59) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp c66a19ed-90c0-4502-ae75-6a4c4ab9f297))
(pad "40" smd rect (at 8.89 -24.13) (size 3.5 1.7) (drill (offset 0.9 0)) (layers "F.Cu" "F.Mask") (tstamp bd085057-7c0e-463a-982b-968a2dc1f0f8))
(pad "41" smd rect (at -2.54 23.9 90) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp b21299b9-3c4d-43df-b399-7f9b08eb5470))
(pad "42" smd rect (at 0 23.9 90) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp 751d823e-1d7b-4501-9658-d06d459b0e16))
(pad "43" smd rect (at 2.54 23.9 90) (size 3.5 1.7) (drill (offset -0.9 0)) (layers "F.Cu" "F.Mask") (tstamp aadc3df5-0e2d-4f3d-b72e-6f184da74c89))
(model "${KIPRJMOD}/../KiCad-RP Pico/RP-Pico-Libraries/Pico.wrl"
(offset (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)

File diff suppressed because it is too large Load Diff

54
hw/boards/README.md Normal file
View File

@ -0,0 +1,54 @@
# MTA1-USB PCB designs
## Boards
The KiCad board projects all follow this directory structure:
| Path | Description |
| --- | --- |
| /project | KiCad 6 project |
| /project/gerbers | PCB release files |
| /project/test | Scripts / gateware used for production tests |
### mta1-usb-v1
![](../../doc/images/mta1-usb-v1.jpg)
This is the first production version of the MTA1-USB design.
Main features:
* Miniaturized hardware the size of a small USB stick.
* The Interface FPGA from the MTA1-USB-DEV design has been replaced with an Interface MCU running a USB-to-Serial converter firmware. This was done to ease development of the FPGA design.
* A touch sensor IC is provided instead of the switch for user presence detection.
* Test pads are provided on the bottom of the board for programming the SPI flash, and also for measuring extra GPIO pins. The 'MTA1-USB-V1-Programmer' board is designed to support this.
### mta1-usb-v1-programmer
![](../../doc/images/mta1-usb-v1-programmer.jpg)
This is a programming jig for the mta1-usb-v1 board.
### mta1-usb-dev
![](../../doc/images/mta1-usb-dev.jpg)
This is the original design, intended as a development platform. It is a larger PCB, that connects to a PC via a USB C cable.
Main features:
* 2 ICE40 FPGAs.
* The first ICE40 (Interface FPGA) is connected to a USB C plug, and is loaded with a gateware that contains a soft USB device. This gateware acts as an USB-to-GPIO interface to the second ICE40.
* The second ICE40 (application FPGA) is loaded with a RISC-V soft core containing the security firmware. User applications are run on this gateware.
* The second ICE40 also has an input button for user presenence detection.
* An FTDI module is included for programming both the Application and Interface FPGAs.
* Both ICE40s can be configured to boot from included SPI flash memories, NVCM, or be live programmed from the FTDI module.
* All GPIO lines between the two FPGAs, as well as all programming signals, are exposed on 0.1" headers for probing.
## Libraries
### mta1-library
This is a shared KiCad library for all boards in the project.
### KiCad-RP Pico
This is a KiCad library for the Raspberry Pi Pico symbol and footprint. It was extracted from the [Kicad-RP-Pico](https://github.com/ncarandini/KiCad-RP-Pico) project. It is licensed under the [Creative Commons CC-BY-SA 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/legalcode), with an exception exempting waving any control of derived works.

View File

@ -0,0 +1,34 @@
(kicad_wks (version 20210606) (generator pl_editor)
(setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)
(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))
(rect (name "") (start 110 60) (end 2 2) (comment "rect around the title block")
)
(rect (name "") (start 0 0 ltcorner) (end 0 0) (repeat 2) (incrx 2) (incry 2))
(line (name "") (start 50 2 ltcorner) (end 50 0 ltcorner) (repeat 30) (incrx 50))
(tbtext "1" (name "") (pos 25 1 ltcorner) (font (size 1.3 1.3)) (repeat 100) (incrx 50))
(line (name "") (start 50 2 lbcorner) (end 50 0 lbcorner) (repeat 30) (incrx 50))
(tbtext "1" (name "") (pos 25 1 lbcorner) (font (size 1.3 1.3)) (repeat 100) (incrx 50))
(line (name "") (start 0 50 ltcorner) (end 2 50 ltcorner) (repeat 30) (incry 50))
(tbtext "A" (name "") (pos 1 25 ltcorner) (font (size 1.3 1.3)) (justify center) (repeat 100) (incry 50))
(line (name "") (start 0 50 rtcorner) (end 2 50 rtcorner) (repeat 30) (incry 50))
(tbtext "A" (name "") (pos 1 25 rtcorner) (font (size 1.3 1.3)) (justify center) (repeat 100) (incry 50))
(tbtext "Date: ${ISSUE_DATE}" (name "") (pos 87 6.9))
(line (name "") (start 110 5.5) (end 2 5.5))
(tbtext "${KICAD_VERSION}" (name "") (pos 109 4.1) (comment "Kicad version")
)
(line (name "") (start 110 8.5) (end 2 8.5))
(tbtext "Rev: ${REVISION}" (name "") (pos 24 6.9) (font bold))
(tbtext "Size: ${PAPER}" (name "") (pos 109 6.9) (comment "Paper format name")
)
(tbtext "Id: ${#}/${##}" (name "") (pos 24 4.1) (comment "Sheet id")
)
(line (name "") (start 110 12.5) (end 2 12.5))
(tbtext "Title: ${TITLE}" (name "") (pos 109 10.7) (font (size 2 2) bold italic))
(tbtext "File: ${FILENAME}" (name "") (pos 109 14.3))
(line (name "") (start 110 18.5) (end 2 18.5))
(tbtext "Sheet: ${SHEETNAME}" (name "") (pos 109 17))
(line (name "") (start 90 8.5) (end 90 5.5))
(line (name "") (start 26 8.5) (end 26 2))
(tbtext "This document describes Open Hardware and is licensed under the CERN-OHL-S v2.\n\nYou may redistribute and modify this source and make products using it under\nthe terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt).\n\nThis source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING\nOF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE.\nPlease see the CERN-OHL-S v2 for applicable conditions." (name "") (pos 107.729 33.0022))
(tbtext "Copyright ${COMPANY} ${COMMENT1}" (name "") (pos 107.729 53.3222) (font (size 2.5 2.5) bold))
)

View File

@ -0,0 +1,15 @@
EESchema-DOCLIB Version 2.0
#
$CMP ICE40UP5K-SG48ITR
D iCE40 UltraPlus FPGA, 5280 LUTs, 1.2V, 48-pin QFN
K FPGA programmable logic
F http://www.latticesemi.com/Products/FPGAandCPLD/iCE40Ultra
$ENDCMP
#
$CMP W25Q80DVSNIG
D 32Mb Serial Flash Memory, Standard/Dual/Quad SPI, SOIC-8
K flash memory SPI
F https://www.winbond.com/resource-files/w25q80dv_revg_07212015.pdf
$ENDCMP
#
#End Doc Library

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
(footprint "0402rgb-1010" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 616EE650)
(attr smd)
(fp_text reference "D" (at 0 0) (layer "F.Fab")
(effects (font (size 0.5 0.5) (thickness 0.075)))
(tstamp 6eaf44a5-2bb8-4e84-ae85-e082a57042dd)
)
(fp_text value "0402rgb-1010" (at 0.0635 -1.54178) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 4e647fa9-4baf-493a-891e-373b7bb90db1)
)
(fp_poly (pts
(xy -0.6096 -0.60198)
(xy -0.29972 -0.60198)
(xy -0.29972 -0.25146)
(xy -0.6096 -0.25146)
(xy -0.6096 -0.45212)
) (layer "F.Paste") (width 0.01) (fill solid) (tstamp 8cd8d6bd-0601-49fc-9009-a437af9b27c1))
(fp_poly (pts
(xy 0.2921 0.25146)
(xy 0.60198 0.25146)
(xy 0.60198 0.60198)
(xy 0.2921 0.60198)
(xy 0.2921 0.40132)
) (layer "F.Paste") (width 0.01) (fill solid) (tstamp c0eb397c-0f0a-48f2-a4a7-a39c38857565))
(fp_poly (pts
(xy 0.28956 -0.60198)
(xy 0.59944 -0.60198)
(xy 0.59944 -0.25146)
(xy 0.28956 -0.25146)
(xy 0.28956 -0.45212)
) (layer "F.Paste") (width 0.01) (fill solid) (tstamp f157df02-fcb0-4ae7-85ca-bfc4444eda90))
(fp_poly (pts
(xy -0.60198 0.24892)
(xy -0.2921 0.24892)
(xy -0.2921 0.59944)
(xy -0.60198 0.59944)
(xy -0.60198 0.39878)
) (layer "F.Paste") (width 0.01) (fill solid) (tstamp f3dab665-64fc-433e-8a62-3743b891ab83))
(fp_line (start 0.6477 0.4191) (end 0.6477 0.65278) (layer "F.SilkS") (width 0.05) (tstamp 4ea989fb-9cda-4210-89d1-fe153727e40c))
(fp_line (start 0.6477 0.65278) (end 0.44704 0.65278) (layer "F.SilkS") (width 0.05) (tstamp f64aa569-ea55-4736-9c96-3bfc2b30ccbd))
(fp_line (start -0.8 -0.8) (end 0.8 -0.8) (layer "F.CrtYd") (width 0.05) (tstamp 05e65b06-4a6d-49f9-a6e8-c5bc53ef3e2a))
(fp_line (start -0.8 0.8) (end 0.8 0.8) (layer "F.CrtYd") (width 0.05) (tstamp 39156b1c-bf91-438d-afb8-2007458c0b8e))
(fp_line (start 0.8 0.8) (end 0.8 -0.8) (layer "F.CrtYd") (width 0.05) (tstamp 914c5b3a-21b5-462d-8f55-ca0b3cc81d2e))
(fp_line (start -0.8 0.8) (end -0.8 -0.8) (layer "F.CrtYd") (width 0.05) (tstamp dd287ecc-48c7-4569-84ef-5639ce72358e))
(fp_line (start -0.6 0.6) (end -0.6 -0.6) (layer "F.Fab") (width 0.1) (tstamp 08f7614b-9649-49c1-b63a-65c3d0a1ef41))
(fp_line (start 0.3 0.6) (end -0.6 0.6) (layer "F.Fab") (width 0.1) (tstamp 27c16a37-d65b-4a83-af1a-5ee1a87062f4))
(fp_line (start 0.3 0.6) (end 0.6 0.3) (layer "F.Fab") (width 0.1) (tstamp 4b62363e-5fde-40c0-ad96-ceb955d367f5))
(fp_line (start 0.6 -0.6) (end 0.6 0.3) (layer "F.Fab") (width 0.1) (tstamp 77068506-5e69-47f6-8c18-559ed07a8a9f))
(fp_line (start -0.6 -0.6) (end 0.6 -0.6) (layer "F.Fab") (width 0.1) (tstamp 9d536585-2f1d-4551-af3b-a0a4a6fa8aee))
(pad "1" smd rect (at 0.38 0.38) (size 0.45 0.45) (layers "F.Cu" "F.Mask") (tstamp 1d4ec9d6-b4f1-4935-a655-c469bc01feb9))
(pad "2" smd rect (at -0.38 0.38) (size 0.45 0.45) (layers "F.Cu" "F.Mask") (tstamp 62faf466-a5e1-4997-954a-e3f3f47e0a99))
(pad "3" smd rect (at -0.38 -0.38) (size 0.45 0.45) (layers "F.Cu" "F.Mask") (tstamp 0ea92114-4add-4ede-abc4-5938831a4fe1))
(pad "4" smd rect (at 0.38 -0.38) (size 0.45 0.45) (layers "F.Cu" "F.Mask") (tstamp 4cfa277c-b6f4-4575-8b74-ea83242e8813))
(model "${KIPRJMOD}/../mta1-library/mta1.pretty/3d_models/1010LED-FC-B1010RGBT-HG v3.step"
(offset (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz -90 0 0))
)
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,582 @@
ISO-10303-21;
HEADER;
/* Generated by software containing ST-Developer
* from STEP Tools, Inc. (www.steptools.com)
*/
FILE_DESCRIPTION(
/* description */ (''),
/* implementation_level */ '2;1');
FILE_NAME(
/* name */
'/Users/user/Desktop/2021-09-04 mullvad fpga/mta1-signer-master/hw/mta
1_signer/pcb/mta1_signer_pmod/lib/kicad-pmod/pmod-conn_6x2.pretty/3d f
ile/Ferriit bead-0603.step',
/* time_stamp */ '2021-09-04T15:08:00+02:00',
/* author */ (''),
/* organization */ (''),
/* preprocessor_version */ 'ST-DEVELOPER v18.1',
/* originating_system */ 'Autodesk Translation Framework v10.10.0.1391',
/* authorisation */ '');
FILE_SCHEMA (('AUTOMOTIVE_DESIGN { 1 0 10303 214 3 1 1 }'));
ENDSEC;
DATA;
#10=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#13,#14,
#15),#485);
#11=SHAPE_REPRESENTATION_RELATIONSHIP('SRR','None',#492,#12);
#12=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#16,#17,#18),#484);
#13=STYLED_ITEM('',(#502),#16);
#14=STYLED_ITEM('',(#503),#17);
#15=STYLED_ITEM('',(#503),#18);
#16=MANIFOLD_SOLID_BREP('Body1',#295);
#17=MANIFOLD_SOLID_BREP('Body2',#296);
#18=MANIFOLD_SOLID_BREP('Body3',#297);
#19=FACE_OUTER_BOUND('',#37,.T.);
#20=FACE_OUTER_BOUND('',#38,.T.);
#21=FACE_OUTER_BOUND('',#39,.T.);
#22=FACE_OUTER_BOUND('',#40,.T.);
#23=FACE_OUTER_BOUND('',#41,.T.);
#24=FACE_OUTER_BOUND('',#42,.T.);
#25=FACE_OUTER_BOUND('',#43,.T.);
#26=FACE_OUTER_BOUND('',#44,.T.);
#27=FACE_OUTER_BOUND('',#45,.T.);
#28=FACE_OUTER_BOUND('',#46,.T.);
#29=FACE_OUTER_BOUND('',#47,.T.);
#30=FACE_OUTER_BOUND('',#48,.T.);
#31=FACE_OUTER_BOUND('',#49,.T.);
#32=FACE_OUTER_BOUND('',#50,.T.);
#33=FACE_OUTER_BOUND('',#51,.T.);
#34=FACE_OUTER_BOUND('',#52,.T.);
#35=FACE_OUTER_BOUND('',#53,.T.);
#36=FACE_OUTER_BOUND('',#54,.T.);
#37=EDGE_LOOP('',(#187,#188,#189,#190));
#38=EDGE_LOOP('',(#191,#192,#193,#194));
#39=EDGE_LOOP('',(#195,#196,#197,#198));
#40=EDGE_LOOP('',(#199,#200,#201,#202));
#41=EDGE_LOOP('',(#203,#204,#205,#206));
#42=EDGE_LOOP('',(#207,#208,#209,#210));
#43=EDGE_LOOP('',(#211,#212,#213,#214));
#44=EDGE_LOOP('',(#215,#216,#217,#218));
#45=EDGE_LOOP('',(#219,#220,#221,#222));
#46=EDGE_LOOP('',(#223,#224,#225,#226));
#47=EDGE_LOOP('',(#227,#228,#229,#230));
#48=EDGE_LOOP('',(#231,#232,#233,#234));
#49=EDGE_LOOP('',(#235,#236,#237,#238));
#50=EDGE_LOOP('',(#239,#240,#241,#242));
#51=EDGE_LOOP('',(#243,#244,#245,#246));
#52=EDGE_LOOP('',(#247,#248,#249,#250));
#53=EDGE_LOOP('',(#251,#252,#253,#254));
#54=EDGE_LOOP('',(#255,#256,#257,#258));
#55=LINE('',#407,#91);
#56=LINE('',#409,#92);
#57=LINE('',#411,#93);
#58=LINE('',#412,#94);
#59=LINE('',#415,#95);
#60=LINE('',#417,#96);
#61=LINE('',#418,#97);
#62=LINE('',#421,#98);
#63=LINE('',#423,#99);
#64=LINE('',#424,#100);
#65=LINE('',#426,#101);
#66=LINE('',#427,#102);
#67=LINE('',#433,#103);
#68=LINE('',#435,#104);
#69=LINE('',#437,#105);
#70=LINE('',#438,#106);
#71=LINE('',#441,#107);
#72=LINE('',#443,#108);
#73=LINE('',#444,#109);
#74=LINE('',#447,#110);
#75=LINE('',#449,#111);
#76=LINE('',#450,#112);
#77=LINE('',#452,#113);
#78=LINE('',#453,#114);
#79=LINE('',#459,#115);
#80=LINE('',#461,#116);
#81=LINE('',#463,#117);
#82=LINE('',#464,#118);
#83=LINE('',#467,#119);
#84=LINE('',#469,#120);
#85=LINE('',#470,#121);
#86=LINE('',#473,#122);
#87=LINE('',#475,#123);
#88=LINE('',#476,#124);
#89=LINE('',#478,#125);
#90=LINE('',#479,#126);
#91=VECTOR('',#333,10.);
#92=VECTOR('',#334,10.);
#93=VECTOR('',#335,10.);
#94=VECTOR('',#336,10.);
#95=VECTOR('',#339,10.);
#96=VECTOR('',#340,10.);
#97=VECTOR('',#341,10.);
#98=VECTOR('',#344,10.);
#99=VECTOR('',#345,10.);
#100=VECTOR('',#346,10.);
#101=VECTOR('',#349,10.);
#102=VECTOR('',#350,10.);
#103=VECTOR('',#357,10.);
#104=VECTOR('',#358,10.);
#105=VECTOR('',#359,10.);
#106=VECTOR('',#360,10.);
#107=VECTOR('',#363,10.);
#108=VECTOR('',#364,10.);
#109=VECTOR('',#365,10.);
#110=VECTOR('',#368,10.);
#111=VECTOR('',#369,10.);
#112=VECTOR('',#370,10.);
#113=VECTOR('',#373,10.);
#114=VECTOR('',#374,10.);
#115=VECTOR('',#381,10.);
#116=VECTOR('',#382,10.);
#117=VECTOR('',#383,10.);
#118=VECTOR('',#384,10.);
#119=VECTOR('',#387,10.);
#120=VECTOR('',#388,10.);
#121=VECTOR('',#389,10.);
#122=VECTOR('',#392,10.);
#123=VECTOR('',#393,10.);
#124=VECTOR('',#394,10.);
#125=VECTOR('',#397,10.);
#126=VECTOR('',#398,10.);
#127=VERTEX_POINT('',#405);
#128=VERTEX_POINT('',#406);
#129=VERTEX_POINT('',#408);
#130=VERTEX_POINT('',#410);
#131=VERTEX_POINT('',#414);
#132=VERTEX_POINT('',#416);
#133=VERTEX_POINT('',#420);
#134=VERTEX_POINT('',#422);
#135=VERTEX_POINT('',#431);
#136=VERTEX_POINT('',#432);
#137=VERTEX_POINT('',#434);
#138=VERTEX_POINT('',#436);
#139=VERTEX_POINT('',#440);
#140=VERTEX_POINT('',#442);
#141=VERTEX_POINT('',#446);
#142=VERTEX_POINT('',#448);
#143=VERTEX_POINT('',#457);
#144=VERTEX_POINT('',#458);
#145=VERTEX_POINT('',#460);
#146=VERTEX_POINT('',#462);
#147=VERTEX_POINT('',#466);
#148=VERTEX_POINT('',#468);
#149=VERTEX_POINT('',#472);
#150=VERTEX_POINT('',#474);
#151=EDGE_CURVE('',#127,#128,#55,.T.);
#152=EDGE_CURVE('',#128,#129,#56,.T.);
#153=EDGE_CURVE('',#130,#129,#57,.T.);
#154=EDGE_CURVE('',#127,#130,#58,.T.);
#155=EDGE_CURVE('',#127,#131,#59,.T.);
#156=EDGE_CURVE('',#132,#130,#60,.T.);
#157=EDGE_CURVE('',#131,#132,#61,.T.);
#158=EDGE_CURVE('',#133,#131,#62,.T.);
#159=EDGE_CURVE('',#134,#132,#63,.T.);
#160=EDGE_CURVE('',#133,#134,#64,.T.);
#161=EDGE_CURVE('',#128,#133,#65,.T.);
#162=EDGE_CURVE('',#129,#134,#66,.T.);
#163=EDGE_CURVE('',#135,#136,#67,.T.);
#164=EDGE_CURVE('',#136,#137,#68,.T.);
#165=EDGE_CURVE('',#138,#137,#69,.T.);
#166=EDGE_CURVE('',#135,#138,#70,.T.);
#167=EDGE_CURVE('',#139,#135,#71,.T.);
#168=EDGE_CURVE('',#140,#138,#72,.T.);
#169=EDGE_CURVE('',#139,#140,#73,.T.);
#170=EDGE_CURVE('',#141,#139,#74,.T.);
#171=EDGE_CURVE('',#142,#140,#75,.T.);
#172=EDGE_CURVE('',#141,#142,#76,.T.);
#173=EDGE_CURVE('',#136,#141,#77,.T.);
#174=EDGE_CURVE('',#137,#142,#78,.T.);
#175=EDGE_CURVE('',#143,#144,#79,.T.);
#176=EDGE_CURVE('',#144,#145,#80,.T.);
#177=EDGE_CURVE('',#146,#145,#81,.T.);
#178=EDGE_CURVE('',#143,#146,#82,.T.);
#179=EDGE_CURVE('',#143,#147,#83,.T.);
#180=EDGE_CURVE('',#148,#146,#84,.T.);
#181=EDGE_CURVE('',#147,#148,#85,.T.);
#182=EDGE_CURVE('',#149,#147,#86,.T.);
#183=EDGE_CURVE('',#150,#148,#87,.T.);
#184=EDGE_CURVE('',#149,#150,#88,.T.);
#185=EDGE_CURVE('',#144,#149,#89,.T.);
#186=EDGE_CURVE('',#145,#150,#90,.T.);
#187=ORIENTED_EDGE('',*,*,#151,.T.);
#188=ORIENTED_EDGE('',*,*,#152,.T.);
#189=ORIENTED_EDGE('',*,*,#153,.F.);
#190=ORIENTED_EDGE('',*,*,#154,.F.);
#191=ORIENTED_EDGE('',*,*,#155,.F.);
#192=ORIENTED_EDGE('',*,*,#154,.T.);
#193=ORIENTED_EDGE('',*,*,#156,.F.);
#194=ORIENTED_EDGE('',*,*,#157,.F.);
#195=ORIENTED_EDGE('',*,*,#158,.T.);
#196=ORIENTED_EDGE('',*,*,#157,.T.);
#197=ORIENTED_EDGE('',*,*,#159,.F.);
#198=ORIENTED_EDGE('',*,*,#160,.F.);
#199=ORIENTED_EDGE('',*,*,#161,.T.);
#200=ORIENTED_EDGE('',*,*,#160,.T.);
#201=ORIENTED_EDGE('',*,*,#162,.F.);
#202=ORIENTED_EDGE('',*,*,#152,.F.);
#203=ORIENTED_EDGE('',*,*,#162,.T.);
#204=ORIENTED_EDGE('',*,*,#159,.T.);
#205=ORIENTED_EDGE('',*,*,#156,.T.);
#206=ORIENTED_EDGE('',*,*,#153,.T.);
#207=ORIENTED_EDGE('',*,*,#161,.F.);
#208=ORIENTED_EDGE('',*,*,#151,.F.);
#209=ORIENTED_EDGE('',*,*,#155,.T.);
#210=ORIENTED_EDGE('',*,*,#158,.F.);
#211=ORIENTED_EDGE('',*,*,#163,.T.);
#212=ORIENTED_EDGE('',*,*,#164,.T.);
#213=ORIENTED_EDGE('',*,*,#165,.F.);
#214=ORIENTED_EDGE('',*,*,#166,.F.);
#215=ORIENTED_EDGE('',*,*,#167,.T.);
#216=ORIENTED_EDGE('',*,*,#166,.T.);
#217=ORIENTED_EDGE('',*,*,#168,.F.);
#218=ORIENTED_EDGE('',*,*,#169,.F.);
#219=ORIENTED_EDGE('',*,*,#170,.T.);
#220=ORIENTED_EDGE('',*,*,#169,.T.);
#221=ORIENTED_EDGE('',*,*,#171,.F.);
#222=ORIENTED_EDGE('',*,*,#172,.F.);
#223=ORIENTED_EDGE('',*,*,#173,.T.);
#224=ORIENTED_EDGE('',*,*,#172,.T.);
#225=ORIENTED_EDGE('',*,*,#174,.F.);
#226=ORIENTED_EDGE('',*,*,#164,.F.);
#227=ORIENTED_EDGE('',*,*,#174,.T.);
#228=ORIENTED_EDGE('',*,*,#171,.T.);
#229=ORIENTED_EDGE('',*,*,#168,.T.);
#230=ORIENTED_EDGE('',*,*,#165,.T.);
#231=ORIENTED_EDGE('',*,*,#173,.F.);
#232=ORIENTED_EDGE('',*,*,#163,.F.);
#233=ORIENTED_EDGE('',*,*,#167,.F.);
#234=ORIENTED_EDGE('',*,*,#170,.F.);
#235=ORIENTED_EDGE('',*,*,#175,.T.);
#236=ORIENTED_EDGE('',*,*,#176,.T.);
#237=ORIENTED_EDGE('',*,*,#177,.F.);
#238=ORIENTED_EDGE('',*,*,#178,.F.);
#239=ORIENTED_EDGE('',*,*,#179,.F.);
#240=ORIENTED_EDGE('',*,*,#178,.T.);
#241=ORIENTED_EDGE('',*,*,#180,.F.);
#242=ORIENTED_EDGE('',*,*,#181,.F.);
#243=ORIENTED_EDGE('',*,*,#182,.T.);
#244=ORIENTED_EDGE('',*,*,#181,.T.);
#245=ORIENTED_EDGE('',*,*,#183,.F.);
#246=ORIENTED_EDGE('',*,*,#184,.F.);
#247=ORIENTED_EDGE('',*,*,#185,.T.);
#248=ORIENTED_EDGE('',*,*,#184,.T.);
#249=ORIENTED_EDGE('',*,*,#186,.F.);
#250=ORIENTED_EDGE('',*,*,#176,.F.);
#251=ORIENTED_EDGE('',*,*,#186,.T.);
#252=ORIENTED_EDGE('',*,*,#183,.T.);
#253=ORIENTED_EDGE('',*,*,#180,.T.);
#254=ORIENTED_EDGE('',*,*,#177,.T.);
#255=ORIENTED_EDGE('',*,*,#185,.F.);
#256=ORIENTED_EDGE('',*,*,#175,.F.);
#257=ORIENTED_EDGE('',*,*,#179,.T.);
#258=ORIENTED_EDGE('',*,*,#182,.F.);
#259=PLANE('',#311);
#260=PLANE('',#312);
#261=PLANE('',#313);
#262=PLANE('',#314);
#263=PLANE('',#315);
#264=PLANE('',#316);
#265=PLANE('',#317);
#266=PLANE('',#318);
#267=PLANE('',#319);
#268=PLANE('',#320);
#269=PLANE('',#321);
#270=PLANE('',#322);
#271=PLANE('',#323);
#272=PLANE('',#324);
#273=PLANE('',#325);
#274=PLANE('',#326);
#275=PLANE('',#327);
#276=PLANE('',#328);
#277=ADVANCED_FACE('',(#19),#259,.T.);
#278=ADVANCED_FACE('',(#20),#260,.T.);
#279=ADVANCED_FACE('',(#21),#261,.T.);
#280=ADVANCED_FACE('',(#22),#262,.T.);
#281=ADVANCED_FACE('',(#23),#263,.T.);
#282=ADVANCED_FACE('',(#24),#264,.F.);
#283=ADVANCED_FACE('',(#25),#265,.T.);
#284=ADVANCED_FACE('',(#26),#266,.T.);
#285=ADVANCED_FACE('',(#27),#267,.T.);
#286=ADVANCED_FACE('',(#28),#268,.T.);
#287=ADVANCED_FACE('',(#29),#269,.T.);
#288=ADVANCED_FACE('',(#30),#270,.F.);
#289=ADVANCED_FACE('',(#31),#271,.T.);
#290=ADVANCED_FACE('',(#32),#272,.T.);
#291=ADVANCED_FACE('',(#33),#273,.T.);
#292=ADVANCED_FACE('',(#34),#274,.T.);
#293=ADVANCED_FACE('',(#35),#275,.T.);
#294=ADVANCED_FACE('',(#36),#276,.F.);
#295=CLOSED_SHELL('',(#277,#278,#279,#280,#281,#282));
#296=CLOSED_SHELL('',(#283,#284,#285,#286,#287,#288));
#297=CLOSED_SHELL('',(#289,#290,#291,#292,#293,#294));
#298=DERIVED_UNIT_ELEMENT(#300,1.);
#299=DERIVED_UNIT_ELEMENT(#487,-3.);
#300=(
MASS_UNIT()
NAMED_UNIT(*)
SI_UNIT(.KILO.,.GRAM.)
);
#301=DERIVED_UNIT((#298,#299));
#302=MEASURE_REPRESENTATION_ITEM('density measure',
POSITIVE_RATIO_MEASURE(7850.),#301);
#303=PROPERTY_DEFINITION_REPRESENTATION(#308,#305);
#304=PROPERTY_DEFINITION_REPRESENTATION(#309,#306);
#305=REPRESENTATION('material name',(#307),#484);
#306=REPRESENTATION('density',(#302),#484);
#307=DESCRIPTIVE_REPRESENTATION_ITEM('Steel','Steel');
#308=PROPERTY_DEFINITION('material property','material name',#494);
#309=PROPERTY_DEFINITION('material property','density of part',#494);
#310=AXIS2_PLACEMENT_3D('placement',#403,#329,#330);
#311=AXIS2_PLACEMENT_3D('',#404,#331,#332);
#312=AXIS2_PLACEMENT_3D('',#413,#337,#338);
#313=AXIS2_PLACEMENT_3D('',#419,#342,#343);
#314=AXIS2_PLACEMENT_3D('',#425,#347,#348);
#315=AXIS2_PLACEMENT_3D('',#428,#351,#352);
#316=AXIS2_PLACEMENT_3D('',#429,#353,#354);
#317=AXIS2_PLACEMENT_3D('',#430,#355,#356);
#318=AXIS2_PLACEMENT_3D('',#439,#361,#362);
#319=AXIS2_PLACEMENT_3D('',#445,#366,#367);
#320=AXIS2_PLACEMENT_3D('',#451,#371,#372);
#321=AXIS2_PLACEMENT_3D('',#454,#375,#376);
#322=AXIS2_PLACEMENT_3D('',#455,#377,#378);
#323=AXIS2_PLACEMENT_3D('',#456,#379,#380);
#324=AXIS2_PLACEMENT_3D('',#465,#385,#386);
#325=AXIS2_PLACEMENT_3D('',#471,#390,#391);
#326=AXIS2_PLACEMENT_3D('',#477,#395,#396);
#327=AXIS2_PLACEMENT_3D('',#480,#399,#400);
#328=AXIS2_PLACEMENT_3D('',#481,#401,#402);
#329=DIRECTION('axis',(0.,0.,1.));
#330=DIRECTION('refdir',(1.,0.,0.));
#331=DIRECTION('center_axis',(0.,0.,-1.));
#332=DIRECTION('ref_axis',(-1.,0.,0.));
#333=DIRECTION('',(-1.,0.,0.));
#334=DIRECTION('',(0.,1.,0.));
#335=DIRECTION('',(-1.,0.,0.));
#336=DIRECTION('',(0.,1.,0.));
#337=DIRECTION('center_axis',(1.,0.,-5.55111512312578E-15));
#338=DIRECTION('ref_axis',(-5.55111512312578E-15,0.,-1.));
#339=DIRECTION('',(5.55111512312578E-15,0.,1.));
#340=DIRECTION('',(-5.55111512312578E-15,0.,-1.));
#341=DIRECTION('',(0.,1.,0.));
#342=DIRECTION('center_axis',(1.38777878078145E-15,0.,1.));
#343=DIRECTION('ref_axis',(1.,0.,-1.38777878078145E-15));
#344=DIRECTION('',(1.,0.,-1.38777878078145E-15));
#345=DIRECTION('',(1.,0.,-1.38777878078145E-15));
#346=DIRECTION('',(0.,1.,0.));
#347=DIRECTION('center_axis',(-1.,0.,0.));
#348=DIRECTION('ref_axis',(0.,0.,1.));
#349=DIRECTION('',(0.,0.,1.));
#350=DIRECTION('',(0.,0.,1.));
#351=DIRECTION('center_axis',(0.,1.,0.));
#352=DIRECTION('ref_axis',(0.,0.,1.));
#353=DIRECTION('center_axis',(0.,1.,0.));
#354=DIRECTION('ref_axis',(1.,0.,0.));
#355=DIRECTION('center_axis',(0.,0.,-1.));
#356=DIRECTION('ref_axis',(-1.,0.,0.));
#357=DIRECTION('',(-1.,0.,0.));
#358=DIRECTION('',(0.,1.,0.));
#359=DIRECTION('',(-1.,0.,0.));
#360=DIRECTION('',(0.,1.,0.));
#361=DIRECTION('center_axis',(1.,0.,-3.46944695195362E-16));
#362=DIRECTION('ref_axis',(-3.46944695195362E-16,0.,-1.));
#363=DIRECTION('',(-3.46944695195362E-16,0.,-1.));
#364=DIRECTION('',(-3.46944695195362E-16,0.,-1.));
#365=DIRECTION('',(0.,1.,0.));
#366=DIRECTION('center_axis',(1.38777878078145E-15,0.,1.));
#367=DIRECTION('ref_axis',(1.,0.,-1.38777878078145E-15));
#368=DIRECTION('',(1.,0.,-1.38777878078145E-15));
#369=DIRECTION('',(1.,0.,-1.38777878078145E-15));
#370=DIRECTION('',(0.,1.,0.));
#371=DIRECTION('center_axis',(-1.,0.,5.55111512312578E-15));
#372=DIRECTION('ref_axis',(5.55111512312578E-15,0.,1.));
#373=DIRECTION('',(5.55111512312578E-15,0.,1.));
#374=DIRECTION('',(5.55111512312578E-15,0.,1.));
#375=DIRECTION('center_axis',(0.,1.,0.));
#376=DIRECTION('ref_axis',(0.,0.,1.));
#377=DIRECTION('center_axis',(0.,1.,0.));
#378=DIRECTION('ref_axis',(1.,0.,0.));
#379=DIRECTION('center_axis',(0.,0.,-1.));
#380=DIRECTION('ref_axis',(-1.,0.,0.));
#381=DIRECTION('',(-1.,0.,0.));
#382=DIRECTION('',(0.,1.,0.));
#383=DIRECTION('',(-1.,0.,0.));
#384=DIRECTION('',(0.,1.,0.));
#385=DIRECTION('center_axis',(1.,0.,0.));
#386=DIRECTION('ref_axis',(0.,0.,-1.));
#387=DIRECTION('',(0.,0.,1.));
#388=DIRECTION('',(0.,0.,-1.));
#389=DIRECTION('',(0.,1.,0.));
#390=DIRECTION('center_axis',(1.38777878078145E-15,0.,1.));
#391=DIRECTION('ref_axis',(1.,0.,-1.38777878078145E-15));
#392=DIRECTION('',(1.,0.,-1.38777878078145E-15));
#393=DIRECTION('',(1.,0.,-1.38777878078145E-15));
#394=DIRECTION('',(0.,1.,0.));
#395=DIRECTION('center_axis',(-1.,0.,-3.46944695195361E-16));
#396=DIRECTION('ref_axis',(-3.46944695195361E-16,0.,1.));
#397=DIRECTION('',(-3.46944695195361E-16,0.,1.));
#398=DIRECTION('',(-3.46944695195361E-16,0.,1.));
#399=DIRECTION('center_axis',(0.,1.,0.));
#400=DIRECTION('ref_axis',(0.,0.,1.));
#401=DIRECTION('center_axis',(0.,1.,0.));
#402=DIRECTION('ref_axis',(1.,0.,0.));
#403=CARTESIAN_POINT('',(0.,0.,0.));
#404=CARTESIAN_POINT('Origin',(0.399999999999999,0.,-0.4));
#405=CARTESIAN_POINT('',(0.399999999999999,0.,-0.400000000000002));
#406=CARTESIAN_POINT('',(-0.400000000000001,0.,-0.4));
#407=CARTESIAN_POINT('',(0.799999999999999,0.,-0.4));
#408=CARTESIAN_POINT('',(-0.400000000000001,0.6,-0.4));
#409=CARTESIAN_POINT('',(-0.400000000000001,0.,-0.4));
#410=CARTESIAN_POINT('',(0.399999999999999,0.6,-0.400000000000002));
#411=CARTESIAN_POINT('',(0.799999999999999,0.6,-0.4));
#412=CARTESIAN_POINT('',(0.399999999999999,0.,-0.400000000000002));
#413=CARTESIAN_POINT('Origin',(0.400000000000003,0.,0.399999999999999));
#414=CARTESIAN_POINT('',(0.400000000000003,0.,0.399999999999999));
#415=CARTESIAN_POINT('',(0.399999999999999,0.,-0.400000000000002));
#416=CARTESIAN_POINT('',(0.400000000000003,0.6,0.399999999999999));
#417=CARTESIAN_POINT('',(0.399999999999999,0.6,-0.400000000000002));
#418=CARTESIAN_POINT('',(0.400000000000003,0.,0.399999999999999));
#419=CARTESIAN_POINT('Origin',(-0.400000000000001,0.,0.400000000000001));
#420=CARTESIAN_POINT('',(-0.400000000000001,0.,0.400000000000001));
#421=CARTESIAN_POINT('',(-0.800000000000001,0.,0.400000000000001));
#422=CARTESIAN_POINT('',(-0.400000000000001,0.6,0.400000000000001));
#423=CARTESIAN_POINT('',(-0.800000000000001,0.6,0.400000000000001));
#424=CARTESIAN_POINT('',(-0.400000000000001,0.,0.400000000000001));
#425=CARTESIAN_POINT('Origin',(-0.400000000000001,0.,-0.4));
#426=CARTESIAN_POINT('',(-0.400000000000001,0.,-0.4));
#427=CARTESIAN_POINT('',(-0.400000000000001,0.6,-0.4));
#428=CARTESIAN_POINT('Origin',(1.11022302462516E-15,0.6,-5.55111512312578E-16));
#429=CARTESIAN_POINT('Origin',(1.11022302462516E-15,0.,-5.55111512312578E-16));
#430=CARTESIAN_POINT('Origin',(0.799999999999999,0.,-0.4));
#431=CARTESIAN_POINT('',(0.799999999999999,0.,-0.4));
#432=CARTESIAN_POINT('',(0.399999999999999,0.,-0.400000000000002));
#433=CARTESIAN_POINT('',(0.799999999999999,0.,-0.4));
#434=CARTESIAN_POINT('',(0.399999999999999,0.6,-0.400000000000002));
#435=CARTESIAN_POINT('',(0.399999999999999,0.,-0.400000000000002));
#436=CARTESIAN_POINT('',(0.799999999999999,0.6,-0.4));
#437=CARTESIAN_POINT('',(0.799999999999999,0.6,-0.4));
#438=CARTESIAN_POINT('',(0.799999999999999,0.,-0.4));
#439=CARTESIAN_POINT('Origin',(0.799999999999999,0.,0.399999999999999));
#440=CARTESIAN_POINT('',(0.799999999999999,0.,0.399999999999999));
#441=CARTESIAN_POINT('',(0.799999999999999,0.,0.399999999999999));
#442=CARTESIAN_POINT('',(0.799999999999999,0.6,0.399999999999999));
#443=CARTESIAN_POINT('',(0.799999999999999,0.6,0.399999999999999));
#444=CARTESIAN_POINT('',(0.799999999999999,0.,0.399999999999999));
#445=CARTESIAN_POINT('Origin',(0.400000000000003,0.,0.4));
#446=CARTESIAN_POINT('',(0.400000000000003,0.,0.399999999999999));
#447=CARTESIAN_POINT('',(-0.800000000000001,0.,0.400000000000001));
#448=CARTESIAN_POINT('',(0.400000000000003,0.6,0.399999999999999));
#449=CARTESIAN_POINT('',(-0.800000000000001,0.6,0.400000000000001));
#450=CARTESIAN_POINT('',(0.400000000000003,0.,0.399999999999999));
#451=CARTESIAN_POINT('Origin',(0.399999999999999,0.,-0.400000000000002));
#452=CARTESIAN_POINT('',(0.399999999999999,0.,-0.400000000000002));
#453=CARTESIAN_POINT('',(0.399999999999999,0.6,-0.400000000000002));
#454=CARTESIAN_POINT('Origin',(0.599999999999999,0.6,-1.11022302462516E-15));
#455=CARTESIAN_POINT('Origin',(0.599999999999999,0.,-1.11022302462516E-15));
#456=CARTESIAN_POINT('Origin',(-0.400000000000001,0.,-0.4));
#457=CARTESIAN_POINT('',(-0.400000000000001,0.,-0.4));
#458=CARTESIAN_POINT('',(-0.800000000000001,0.,-0.4));
#459=CARTESIAN_POINT('',(0.799999999999999,0.,-0.4));
#460=CARTESIAN_POINT('',(-0.800000000000001,0.6,-0.4));
#461=CARTESIAN_POINT('',(-0.800000000000001,0.,-0.4));
#462=CARTESIAN_POINT('',(-0.400000000000001,0.6,-0.4));
#463=CARTESIAN_POINT('',(0.799999999999999,0.6,-0.4));
#464=CARTESIAN_POINT('',(-0.400000000000001,0.,-0.4));
#465=CARTESIAN_POINT('Origin',(-0.400000000000001,0.,0.400000000000001));
#466=CARTESIAN_POINT('',(-0.400000000000001,0.,0.400000000000001));
#467=CARTESIAN_POINT('',(-0.400000000000001,0.,-0.4));
#468=CARTESIAN_POINT('',(-0.400000000000001,0.6,0.400000000000001));
#469=CARTESIAN_POINT('',(-0.400000000000001,0.6,-0.4));
#470=CARTESIAN_POINT('',(-0.400000000000001,0.,0.400000000000001));
#471=CARTESIAN_POINT('Origin',(-0.800000000000001,0.,0.400000000000001));
#472=CARTESIAN_POINT('',(-0.800000000000001,0.,0.400000000000001));
#473=CARTESIAN_POINT('',(-0.800000000000001,0.,0.400000000000001));
#474=CARTESIAN_POINT('',(-0.800000000000001,0.6,0.400000000000001));
#475=CARTESIAN_POINT('',(-0.800000000000001,0.6,0.400000000000001));
#476=CARTESIAN_POINT('',(-0.800000000000001,0.,0.400000000000001));
#477=CARTESIAN_POINT('Origin',(-0.800000000000001,0.,-0.4));
#478=CARTESIAN_POINT('',(-0.800000000000001,0.,-0.4));
#479=CARTESIAN_POINT('',(-0.800000000000001,0.6,-0.4));
#480=CARTESIAN_POINT('Origin',(-0.600000000000001,0.6,8.32667268468867E-16));
#481=CARTESIAN_POINT('Origin',(-0.600000000000001,0.,8.32667268468867E-16));
#482=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#486,
'DISTANCE_ACCURACY_VALUE',
'Maximum model space distance between geometric entities at asserted c
onnectivities');
#483=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#486,
'DISTANCE_ACCURACY_VALUE',
'Maximum model space distance between geometric entities at asserted c
onnectivities');
#484=(
GEOMETRIC_REPRESENTATION_CONTEXT(3)
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#482))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#486,#488,#489))
REPRESENTATION_CONTEXT('','3D')
);
#485=(
GEOMETRIC_REPRESENTATION_CONTEXT(3)
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#483))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#486,#488,#489))
REPRESENTATION_CONTEXT('','3D')
);
#486=(
LENGTH_UNIT()
NAMED_UNIT(*)
SI_UNIT(.MILLI.,.METRE.)
);
#487=(
LENGTH_UNIT()
NAMED_UNIT(*)
SI_UNIT($,.METRE.)
);
#488=(
NAMED_UNIT(*)
PLANE_ANGLE_UNIT()
SI_UNIT($,.RADIAN.)
);
#489=(
NAMED_UNIT(*)
SI_UNIT($,.STERADIAN.)
SOLID_ANGLE_UNIT()
);
#490=SHAPE_DEFINITION_REPRESENTATION(#491,#492);
#491=PRODUCT_DEFINITION_SHAPE('',$,#494);
#492=SHAPE_REPRESENTATION('',(#310),#484);
#493=PRODUCT_DEFINITION_CONTEXT('part definition',#498,'design');
#494=PRODUCT_DEFINITION('Ferriit bead','Ferriit bead v1',#495,#493);
#495=PRODUCT_DEFINITION_FORMATION('',$,#500);
#496=PRODUCT_RELATED_PRODUCT_CATEGORY('Ferriit bead v1',
'Ferriit bead v1',(#500));
#497=APPLICATION_PROTOCOL_DEFINITION('international standard',
'automotive_design',2009,#498);
#498=APPLICATION_CONTEXT(
'Core Data for Automotive Mechanical Design Process');
#499=PRODUCT_CONTEXT('part definition',#498,'mechanical');
#500=PRODUCT('Ferriit bead','Ferriit bead v1',$,(#499));
#501=PRESENTATION_STYLE_ASSIGNMENT((#504));
#502=PRESENTATION_STYLE_ASSIGNMENT((#505));
#503=PRESENTATION_STYLE_ASSIGNMENT((#506));
#504=SURFACE_STYLE_USAGE(.BOTH.,#507);
#505=SURFACE_STYLE_USAGE(.BOTH.,#508);
#506=SURFACE_STYLE_USAGE(.BOTH.,#509);
#507=SURFACE_SIDE_STYLE('',(#510));
#508=SURFACE_SIDE_STYLE('',(#511));
#509=SURFACE_SIDE_STYLE('',(#512));
#510=SURFACE_STYLE_FILL_AREA(#513);
#511=SURFACE_STYLE_FILL_AREA(#514);
#512=SURFACE_STYLE_FILL_AREA(#515);
#513=FILL_AREA_STYLE('Steel - Satin',(#516));
#514=FILL_AREA_STYLE('Steel - Satin',(#517));
#515=FILL_AREA_STYLE('Lead - Satin',(#518));
#516=FILL_AREA_STYLE_COLOUR('Steel - Satin',#519);
#517=FILL_AREA_STYLE_COLOUR('Steel - Satin',#520);
#518=FILL_AREA_STYLE_COLOUR('Lead - Satin',#521);
#519=COLOUR_RGB('Steel - Satin',0.627450980392157,0.627450980392157,0.627450980392157);
#520=COLOUR_RGB('Steel - Satin',0.47843137254902,0.392156862745098,0.266666666666667);
#521=COLOUR_RGB('Lead - Satin',0.815686274509804,0.815686274509804,0.815686274509804);
ENDSEC;
END-ISO-10303-21;

Some files were not shown because too many files have changed in this diff Show More