forked from M-Labs/artiq
1
0
Fork 0

Compare commits

...

190 Commits

Author SHA1 Message Date
David Nadlinger 56c5637230 gui: Fix crash when quickly opening/closing applets
Quickly closing/reopening applets (e.g. quickly clicking the checkbox
on an entire folder of applets) would previously lead to an occasional
KeyError on the self.dock_to_item access in on_dock_closed, as close()
would be invoked more than once.

The geometry/checked state handling can potentially be cleaned up
further, but at least this avoid the crash.
2019-03-11 20:39:37 +08:00
whitequark 73ebebf6d7 compiler: monomorphize casts first, but more carefully.
This reverts 425cd7851, which broke the use of casts to define
integer width.

Instead of it, two steps are taken:
  * First, literals are monomorphized, leading to predictable result.
  * Second, casts are monomorphized, in a top-bottom way. I.e.
    consider the expression `int64(round(x))`. If round() was visited
    first, the intermediate precision would be 32-bit, which is
    clearly undesirable. Therefore, contextual rules should take
    priority over non-contextual ones.

Fixes #1252.
2019-02-07 20:58:10 +08:00
whitequark faef018b9d compiler: first monomorphize ints, then casts.
Fixes #1242.
2019-02-07 20:58:06 +08:00
David Nadlinger aa6542fecf conda: Require recent aiohttp
artiq_influxdb doesn't work with aiohttp 0.17 (anymore), as the
ClientSession API changed. I have not looked up precisely which
is the first version that works, but 3.x has been out for almost
a year and is available on the Anaconda/conda-forge channels.
2019-02-07 10:42:02 +08:00
Sebastien Bourdeauducq 87f296c0b5 monkey_patches: disable for Python >= 3.6.7
3.6 >=7 are fixed
3.7.0 is incompatible with the monkey patches and they do more harm than good
3.7 >=1 are fixed
2019-01-15 20:34:40 +08:00
Sebastien Bourdeauducq de89288b63 update copyright year 2019-01-05 15:04:45 +08:00
Sebastien Bourdeauducq af1f87833a manual: move to correct directory for building rust crates. Closes #1222 2018-12-21 10:37:42 +08:00
Drew f301ad814d tdr.py: typo (#1220) 2018-12-19 02:47:59 +08:00
Sebastien Bourdeauducq c894fe028c ctlmgr: do not raise exceptions in Controllers.__setitem__. Closes #1198 2018-12-01 18:10:52 +08:00
Sebastien Bourdeauducq 7c6eee22a8 language: fix syscall arg handling 2018-11-30 17:59:50 +08:00
Robert Jördens f2eafa89fb manual: deprecate old release
closes #863
2018-11-21 15:03:16 +08:00
whitequark dbd1cb9e04 firmware: fix another TOCTTOU race in sync/async RPC code. 2018-11-13 01:00:22 +08:00
whitequark ae88c1328b firmware: fix TOCTTOU race in sync/async RPC code.
Before this commit, the main loop in session code was laid like:

  1. process_kern_queued_rpc
  2. process_host_message
  3. process_kern_message

If a host message (such as an RPC reply) caused the kernel to exit,
then any async RPCs would not complete, since RunFinished immediately
shuts down the kernel.

Fix this by reordering 1 and 2.
2018-11-13 01:00:18 +08:00
Sebastien Bourdeauducq ad07274a1b Revert "relax test_pulse_rate"
This reverts commit eadb39c283.

Seems the performance loss was caused by conda installing the wrong llvmlite-artiq,
which is now fixed.
2018-11-03 20:15:12 +08:00
Sebastien Bourdeauducq f96084e88d conda: unbreak llvmlite-artiq dependency
Go figure why conda didn't error out before 3.7 was tagged...
2018-11-02 19:29:47 +08:00
Sebastien Bourdeauducq ed9815da92 test: relax network transfer rates 2018-11-02 18:25:48 +08:00
Sebastien Bourdeauducq eadb39c283 relax test_pulse_rate
Inexplicably started failing after 9afe84ab79.
2018-11-02 14:41:29 +08:00
Sebastien Bourdeauducq 5a3d12f07b vivado timing test 2018-10-22 11:34:16 +08:00
Sebastien Bourdeauducq 8c891c43a8 RELEASE_NOTES: 3.7 2018-10-21 15:21:32 +08:00
Sebastien Bourdeauducq eb6bc995cc conda: bump migen
BusSychronizer fix
2018-10-21 14:31:38 +08:00
Marius Weber c0d89db677 Tpz fixes (#1178)
*   flake8
*   fix TPZ constructor after move to asyncio
*   Tcube fix docummentation in set_channel_enable_state
2018-10-20 20:49:44 +08:00
Sebastien Bourdeauducq 6d7790844d typo (#1179) 2018-10-20 20:40:29 +08:00
Robert Jördens 9afe84ab79 CONTRIBUTING: correct default licensing 2018-10-17 09:54:53 +08:00
whitequark dbf4e78087 Fix missing import. 2018-09-20 11:03:18 +00:00
whitequark da01a03a6e Fix b91822ff. 2018-09-20 10:01:32 +00:00
whitequark 98e61e4d4d firmware: update smoltcp.
This adds TCP window scaling and ARP cache GC support.
2018-09-20 09:50:29 +00:00
whitequark b91822ffe6 firmware: migrate to Rust 1.28.0.
See 2648b1b7 and bdd18de2.
2018-09-20 09:45:19 +00:00
Sebastien Bourdeauducq fb1dfcf372 firmware: Use larger ARP cache
See b482f5feae
2018-09-19 22:10:16 +08:00
Sebastien Bourdeauducq c83e22c11c test: add test for short RTIO input gate
Based on https://github.com/m-labs/artiq/pull/1136

See also https://github.com/m-labs/artiq/issues/1137
2018-08-28 15:44:54 +08:00
Sebastien Bourdeauducq 12a1a8ee97 conda: work around 'received dictionary as spec' conda bug 2018-08-19 00:15:12 +08:00
whitequark 77d511dc37 compiler: skip functional values in attribute writeback.
Fixes #1088.
2018-08-10 23:27:41 +08:00
Robert Jördens df2322422d browser: handle windows file urls for feeding h5py
close #1014
2018-08-07 12:58:21 +02:00
whitequark 0982c965b1 compiler: handle async RPC as last statement in try block.
Fixes #1107.
2018-08-07 07:08:02 +00:00
whitequark 10d0c6df00 rpc_proto: serialize keywords correctly.
Fixes #1109.
2018-08-07 06:48:34 +00:00
whitequark e8ff55791c Fix tests after a74958f0. 2018-08-07 06:07:22 +00:00
whitequark f10980de8d ksupport: raise RuntimeError on reraise with no inflight exception.
Fixes #1123.
2018-08-07 06:01:53 +00:00
Sebastien Bourdeauducq c659ae0681 firmware: actually compact in config::compact(). 2018-08-02 11:45:30 +08:00
David Nadlinger a6b61f0c1d pyon: Correctly deserialize bare NaNs
This also fixes (non-numpy) lists containing NaNs.

Previously, accidentally storing a NaN in a dataset would
bring down large parts of the system.
2018-07-30 18:26:04 +08:00
Sebastien Bourdeauducq 17665c7271 test_rtio: relax ClockGeneratorLoopback performance requirements 2018-07-09 18:11:31 +08:00
Sebastien Bourdeauducq 8210ee61cf use tokenize.open() to open Python source files
Fixes encoding issues especially with device databases modified in obscure editors.
2018-07-07 17:11:23 +08:00
whitequark 3e7cdaa5d7 runtime: fix size values for bytes and bytearray RPCs.
Fixes #1076.
2018-06-21 00:52:15 +00:00
whitequark cccadd0a55 compiler: support conversion of list to bytearray and bytes.
Fixes #1077.
2018-06-21 00:41:55 +00:00
Sebastien Bourdeauducq e8092f6f11 conda: use h5py 2.8
For some reason, conda installs 2.7 by default, which causes messages such as:
/home/sb/miniconda3/envs/py35/lib/python3.5/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
2018-06-11 10:15:27 +08:00
whitequark 408734b776 artiq_rpctool: use inspect.formatargspec instead of a NIH formatter.
Fixes #1029.
2018-06-11 09:45:10 +08:00
whitequark 36204a97d8 compiler: don't crash when quoting builtin functions.
Fixes #1051.
2018-06-06 11:43:04 +08:00
Sebastien Bourdeauducq 1afcf8b459 monkey_patches: work around Python issue 33678. Closes #1016 2018-05-29 18:07:49 +08:00
whitequark 66a7e09937 conda: fix installation path in artiq-board. 2018-05-26 11:37:41 +08:00
whitequark 086b5ff416 conda: add ignore_prefix_files in the outputs section too. 2018-05-26 11:36:24 +08:00
whitequark 2f4064ef0c conda: override --install-lib, not --prefix. 2018-05-26 11:34:35 +08:00
whitequark e7b3876eb6 Fix non-exception-safe finally: handlers.
Fixes #1013.
2018-05-25 14:07:56 +08:00
whitequark 5f3cb1263c conda: remove lib/python3.5 from artiq package paths as well.
Following c9287cfc.
2018-05-25 11:31:51 +08:00
whitequark d0e5aec862 compiler: handle direct calls to class methods.
Fixes #1005.
2018-05-25 10:07:50 +08:00
whitequark 21406a585e conda: put `noarch: python` on correct artiq-board package.
Fixes #989.
2018-05-25 10:03:33 +08:00
whitequark 02f6727fa2 language: scan functions are not supported on core device.
Closes #1009.
2018-05-23 10:46:29 +08:00
Sebastien Bourdeauducq 5667cef667 manual: add warning about developing section being for developers 2018-05-18 14:19:13 +08:00
Sebastien Bourdeauducq 1c4c5c9d96 coredevice/dds: fix bus_channel doc 2018-05-13 23:41:19 +08:00
Sebastien Bourdeauducq bb91582acc coredevice/dds: fix init_duration_mu and init_sync_duration_mu 2018-05-13 23:39:44 +08:00
whitequark 9c6978be84 Update LLVM to 6.0.0 and Rust to 1.25.0. 2018-05-06 15:05:00 +00:00
whitequark 9589decd54 Update log_buffer.
Closes #986.
2018-05-06 14:30:54 +00:00
whitequark a4fd9ad3e1 Unbreak 9dc7efef. 2018-05-06 11:14:44 +08:00
whitequark 88134db35a compiler: transparently handle Windows newlines in RunTool. 2018-05-06 11:14:37 +08:00
Sebastien Bourdeauducq 0faf781d5f worker: keep sys.modules untouched until the end of examine() 2018-05-02 12:50:54 +08:00
Sebastien Bourdeauducq 840a401c5e worker: python docs recommend not replacing sys.modules 2018-05-02 12:49:03 +08:00
Sebastien Bourdeauducq 6a0bba1d89 worker: restore sys.modules in examine() (#976) 2018-05-02 12:33:32 +08:00
Sebastien Bourdeauducq b84a2a1eeb Revert "tools/file_import: restore sys.modules. Closes #976"
This reverts commit 5f3e417bb5.
2018-05-02 12:33:23 +08:00
Sebastien Bourdeauducq 5f3e417bb5 tools/file_import: restore sys.modules. Closes #976 2018-05-01 22:17:16 +08:00
whitequark 843f871fd1 conda: artiq-board should be noarch, like artiq itself.
Fixes #989.
2018-04-28 09:58:23 +08:00
whitequark 04e8d379b8 Unbreak f35f1001. 2018-04-28 09:58:07 +08:00
whitequark 44e5d0804e compiler: don't crash printing locations of specialized functions.
Fixes #987.
2018-04-28 09:57:59 +08:00
whitequark 1e8cc731b6 firmware: don't truncate queued RPCs (fixes #985). 2018-04-21 23:01:07 +00:00
whitequark 2c12e150f3 compiler: do not try to re-coerce fully coerced numerics. 2018-04-21 23:01:05 +00:00
whitequark 84d807a5e4 runtime: fix race condition in log extraction code paths (#979).
The core device used to panic if certain combinations of borrows
of the log buffer happened. Now they all use .try_borrow_mut().
2018-04-21 11:55:10 +00:00
whitequark 4c65fb79b9 Commit missing part of b4e3c30d. 2018-04-21 11:55:10 +00:00
whitequark d11f66291c compiler: desugar x != y into not x == y (fixes #974). 2018-04-20 20:22:47 +08:00
Sebastien Bourdeauducq 66817e3b82 doc: add note about Sinara requiring ARTIQ-4+ 2018-04-12 15:18:04 +08:00
Sebastien Bourdeauducq e93a07bc8d doc: update Sinara information 2018-04-12 15:15:07 +08:00
Sebastien Bourdeauducq 5dfd0e4701 conda: fix artiq-board (again) 2018-04-10 08:34:41 +08:00
Sebastien Bourdeauducq 6972ba4ee3 conda: fix artiq-board 2018-04-09 19:32:58 +08:00
Sebastien Bourdeauducq 3c678ad351 conda: split build/run requirements for artiq-board. 2018-04-08 21:03:35 +08:00
Sebastien Bourdeauducq 8c654748fa conda: put requirements on correct artiq-board package. 2018-04-08 21:01:50 +08:00
Sebastien Bourdeauducq 9630833033 runtime: do not reset RTIO PHY on core.reset(). Closes #971 2018-03-28 10:51:33 +08:00
Sebastien Bourdeauducq a844a3350e firmware: reset local RTIO PHYs on startup (#958) 2018-03-22 16:30:08 +08:00
Sebastien Bourdeauducq 394b66cd8c RELEASE_NOTES: update 2018-03-22 11:17:25 +08:00
Sebastien Bourdeauducq 41ae1d8e77 conda: bump misoc 2018-03-22 11:16:50 +08:00
Sebastien Bourdeauducq 35b70b3123 ttl_serdes_generic: fix/upgrade test 2018-03-20 16:47:40 +08:00
whitequark 24925f1c9e conda: mark the artiq-build output package as noarch, not toplevel.
This also changes `noarch: python` to `noarch: generic` since
this is semantically correct; the bitstream/firmware packages
contain no Python code.

Fixes #960.
2018-03-15 23:17:23 +00:00
whitequark ba017147fc compiler: do not pass files to external tools while they are opened.
This fixes access violations on Windows that are present both with
input and output files. For some reason, Cygwin-compiled binutils
did not exhibit this problem, but MSYS-compiled binutils do.

Fixes #961.
2018-03-15 22:21:57 +00:00
whitequark 3904138c20 Remove merge artifact. 2018-03-14 04:21:10 +00:00
Sebastien Bourdeauducq ce2b5a97cb rtio/ttl_serdes_7series: reset IOSERDES (#958) 2018-03-14 09:01:56 +08:00
Sebastien Bourdeauducq 595d374f07 update copyright year 2018-03-13 00:25:35 +08:00
whitequark c09269a323 conda: handle ARTIQ_VARIANT=phaser. 2018-03-11 18:59:54 +00:00
Sebastien Bourdeauducq 59fdb32b7b conda: bump misoc 2018-03-10 22:59:09 +08:00
Sebastien Bourdeauducq 7337842ff9 runtime: add a missing overflow flag reset 2018-03-04 01:05:18 +08:00
Sebastien Bourdeauducq 87b51cbcc2 doc: DMA can also raise RTIOUnderflow 2018-03-04 01:04:11 +08:00
whitequark 232940e17f conda: don't use globs in file list. 2018-02-27 17:40:41 +00:00
Robert Jördens 806d583153 kc705_sma_spi: fix cri_con 2018-02-27 10:48:10 +00:00
whitequark 015189b2ae conda: modernize build.sh. 2018-02-26 16:25:49 +00:00
whitequark d0d150d974 conda: add back py_ prefix in dependencies. 2018-02-26 15:48:31 +00:00
whitequark 81f0efea9b conda: use a single artiq-board package. 2018-02-26 15:09:28 +00:00
Sebastien Bourdeauducq cb605cf014 use new misoc identifier 2018-02-22 11:17:57 +08:00
Sebastien Bourdeauducq 1568e55388 conda: bump misoc (#902) 2018-02-22 11:15:14 +08:00
whitequark fbb58b5c8a compiler: reject calls with unexpected keyword arguments.
Fixes #924.
2018-02-21 19:39:50 +08:00
whitequark 92c94c1f62 firmware: make network tracing runtime switchable. 2018-02-14 23:18:58 +00:00
Robert Jördens 9db30ce8dc conda: pin openocd build 2018-01-30 11:12:33 +01:00
Robert Jördens 49a265453d conda: sync artiq and artiq-dev
c.f. 4c22d64e
2018-01-30 11:03:55 +01:00
Sebastien Bourdeauducq e1aafcbb4f artiq_flash: add --preinit-command for buildbot compatibility 2018-01-30 17:35:48 +08:00
Sebastien Bourdeauducq 2548e9d833 RELEASE_NOTES: add 3.4 entry 2018-01-30 17:35:14 +08:00
whitequark b92b00a1c8 Update smoltcp.
Fixes #902.
2018-01-30 03:30:53 +00:00
Sebastien Bourdeauducq bfb03fdbba RELEASE_NOTES: 3.3 2018-01-28 00:18:24 +08:00
Sebastien Bourdeauducq 59fe69a4b3 conda: use new migen 2018-01-27 23:13:58 +08:00
Sebastien Bourdeauducq 8276c6588b conda: use misoc release 2018-01-27 22:22:55 +08:00
whitequark aa64b8ad7a runtime: build with -Cpanic=unwind.
This is required for backtraces to function. I'm not sure how it
turned out that master had -Cpanic=abort.
2018-01-26 23:01:54 +00:00
whitequark 6f7771cb01 Fix 3313e997. 2018-01-27 00:31:59 +08:00
whitequark d2e9ea8de6 test: fix test_worker to work when deprecation warnings are emitted. 2018-01-27 00:31:58 +08:00
Robert Jördens a85fd13c21 conda: bump misoc, close #905
* cherry-pick c9b36e35
* also pin migen as we don't do .dev tags anymore
2018-01-25 19:34:47 +01:00
Sebastien Bourdeauducq 5f2256cb25 conda: build artiq- as noarch: python, like artiq itself (#894) 2018-01-17 10:12:06 +08:00
Sebastien Bourdeauducq 917477f937 examples: update KC705 DNS (used for CI) 2018-01-17 09:41:47 +08:00
whitequark 4f3e7af8d5 doc: Rust uses recursive submodules (brrr).
[ci skip]
2018-01-10 01:28:40 +00:00
whitequark 3b82c585d1 doc: update Rust version.
[ci skip]
2018-01-10 01:28:39 +00:00
Sebastien Bourdeauducq a433794483 Work around another conda bug. 2018-01-09 17:04:33 +08:00
Sebastien Bourdeauducq 6641f4c1ac conda: use tagged migen/misoc 2018-01-09 15:39:27 +08:00
whitequark 24562d232e compiler: don't die if addr2line cannot extract line from backtrace.
Fixes #885.
2018-01-08 22:02:38 +00:00
whitequark 40b9a84a2b firmware: update smoltcp. 2018-01-08 22:02:27 +00:00
whitequark 46218c1fff conda: update rustc to 1.23.0. 2018-01-08 21:55:26 +00:00
whitequark 3ba82cf19c firmware: clean up makefiles. 2018-01-03 08:20:45 +00:00
Sebastien Bourdeauducq e14626e432 conda: bump migen 2017-12-29 11:11:18 +08:00
Sebastien Bourdeauducq 4ae93d4fd8 conda: bump misoc 2017-12-29 01:41:40 +08:00
Robert Jördens 66d1647efd spi: register clk 2017-12-29 01:40:45 +08:00
whitequark e6306b712d firmware: fix a typo replacing spiflash::SECTOR_SIZE with PAGE_SIZE. 2017-12-29 01:37:34 +08:00
Sebastien Bourdeauducq 14a90e5386 firmware: enlarge bootloader partition to 4 sectors. 2017-12-29 01:37:11 +08:00
Sebastien Bourdeauducq 00c9b20d1e firmware: remove bitflags references from Cargo.lock
Only master needs bitflags.
2017-12-28 12:28:37 +08:00
Sebastien Bourdeauducq 8c19d90179 firmware: prepare config block for access from BIOS/bootloader.
This is in 3.2 so that users lose storage only once.
2017-12-28 12:23:18 +08:00
whitequark 135c138ec3 runtime: remove borrow_mut!() in favor of backtraces. 2017-12-28 12:06:29 +08:00
whitequark d419ccdeca compiler: do not permit str(...). (#878) 2017-12-27 11:48:21 +08:00
Sebastien Bourdeauducq 246a2bb3e1 RELEASE_NOTES: add 3.2 entry 2017-12-27 10:52:12 +08:00
whitequark 26dbf0841c conda: ship runtime.elf in board-specific packages.
This is so that backtraces may be symbolized.
2017-12-27 10:48:35 +08:00
whitequark 7af02787e0 conda: bump rustc version requirement. 2017-12-27 10:45:52 +08:00
whitequark 4bda29f863 compiler: fix typo in a0a2650f. 2017-12-27 10:44:26 +08:00
whitequark c44d08a826 conda: update llvmlite-artiq dependency. 2017-12-27 10:44:11 +08:00
whitequark fbf7e70ef8 compiler: do not ever emit !tbaa on invoke instructions. 2017-12-27 10:44:03 +08:00
whitequark ca48c29a8b compiler: update for llvmlite 0.20.0. 2017-12-27 10:43:55 +08:00
whitequark c9be535ba5 compiler: do not use invoke for calls to nounwind ffi functions.
Otherwise, declarations such as:

  @syscall(flags={"nounwind", "nowrite"})
  def foo(...):

trip an LLVM assert because the invoke instruction and the !tbaa
metadata are no longer compatible since LLVM 4.0.
2017-12-27 10:43:48 +08:00
whitequark 3f8dc0233a runtime: update smoltcp. 2017-12-27 10:43:32 +08:00
whitequark 30b7bcf362 Update to LLVM 4.0. 2017-12-27 10:43:04 +08:00
whitequark 99bc18dcd7 runtime: fix some final flash storage issues. 2017-12-27 10:42:57 +08:00
whitequark 65204a091f runtime: we're growing, put storage at 1M instead of 512K. 2017-12-27 10:42:21 +08:00
whitequark 2fd3b3324a runtime: ensure flash storage never overlaps with runtime sections. 2017-12-27 10:41:13 +08:00
whitequark 7f04e75042 runtime: simplify. NFC. 2017-12-27 10:38:05 +08:00
whitequark e364213b62 runtime: remove accidentally committed parts of a Makefile. 2017-12-27 10:37:56 +08:00
whitequark 99bb1b0b70 runtime: print (address-only) backtraces on core device panics. 2017-12-27 10:37:44 +08:00
whitequark b72178756e firmware: fix compatibility with newer rustc. NFC. 2017-12-27 10:35:52 +08:00
whitequark 6cbf8786d8 runtime: get rid of config_dummy.rs. NFC.
Use the same strategy as elsewhere.
2017-12-27 10:35:36 +08:00
whitequark 0ede5d8638 doc: developing: show how to make clang source builds faster. 2017-12-27 10:33:43 +08:00
whitequark 231bf77b43 runtime: update smoltcp. 2017-12-19 23:52:50 +08:00
Sebastien Bourdeauducq df2f0ead4a runtime: no startup_clock config is not an error 2017-12-14 18:50:08 +08:00
whitequark 16d49f38c1 artiq_pcap: still grab the file if the command fails. 2017-12-11 07:31:58 +08:00
whitequark 3f0277197f runtime: update smoltcp. 2017-12-11 07:31:30 +08:00
Robert Jördens e02dc834e6 doc: clean up artiq-dev installation instructions
Add a heading to the openocd setup instruction so that it is
clearly distinguishable from the openocd installation. Otherwise people
"re-install" openocd the wrong way.
2017-12-11 07:31:02 +08:00
Robert Jördens 6cb7f2e8e2 conda/artiq-dev: fix channel list
Now, with conda 4.1 packages are sorted by channel, version, build
number in decreasing priority. The highest matching package is
taken. https://conda.io/docs/user-guide/tasks/manage-channels.html

For the artiq-dev environment, the m-labs/label/dev channel should be
first, then the main channel, then defaults, and then conda-forge
(community supported packages).

closes #864
2017-12-11 07:30:36 +08:00
Sebastien Bourdeauducq 4c2f25e85e RELEASE_NOTES: 3.1 2017-12-07 13:12:02 +08:00
Robert Jördens 2c85597daa test_performance: relax network speed to 2 MB/s
At QUARTIQ I am getting 2.4/2.3 MB/s and with single switch at M-Labs we
apparently regularly met 2.2/2.2 MB/s. But with the current multiple
switches and one of them being a problematic switch that triggered #837
it looks like it is a tad slower.

http://buildbot.m-labs.hk/builders/artiq/builds/1818/steps/python_coverage_1/logs/stdio
2017-12-07 12:24:14 +08:00
Sebastien Bourdeauducq 76a908c8a9 backport Ethernet bugfixes from master 2017-12-07 12:21:21 +08:00
whitequark 0e5a5441aa runtime: remove UDP-related code. 2017-12-07 12:16:33 +08:00
Sebastien Bourdeauducq 45f510bcdc phaser: remove ad9154 from mem_map 2017-11-29 18:23:50 +08:00
Sebastien Bourdeauducq 7e5a301a27 manual: fix formatting problem 2017-11-25 14:41:36 +08:00
Sebastien Bourdeauducq 14714d3f9d Hack-patch Sphinx so that ARTIQ-Python types are correctly printed.
Modification proposed to Sphinx but my issue is getting ignored.

Closes #741
2017-11-25 14:37:21 +08:00
Sebastien Bourdeauducq 25d3fc1e55 sawg: fix typo 2017-11-22 20:06:16 +08:00
Sebastien Bourdeauducq f83cf8d1bb artiq_influxdb: use aiohttp.ClientSession. Closes #829 2017-11-22 17:32:10 +08:00
Sebastien Bourdeauducq 8ebca38323 runtime: fix rtio::log 2017-11-03 09:25:56 +08:00
Sebastien Bourdeauducq 0c47f83634 clean up rtio_log 2017-11-03 01:13:07 +08:00
whitequark f0937bde16 runtime: update smoltcp. 2017-10-30 16:39:25 +08:00
whitequark 3ec1850949 runtime: update smoltcp. 2017-10-30 15:43:28 +08:00
whitequark 0d79b7d292 test: verify no network performance regression from current 2.2 MB/s. 2017-10-30 15:43:28 +08:00
whitequark 3e96e0b10d runtime: parse the "ip" configuration as IP, not CIDR.
Or it defaults to the default IP on settings that were previously
perfectly valid.
2017-10-30 15:43:28 +08:00
Sebastien Bourdeauducq 6902868d58 gui: remove '.0' in background logo 2017-10-26 12:53:35 +08:00
whitequark 89b7c9e091 Update smoltcp.
Fixes #840.
2017-10-25 10:57:42 +08:00
Sebastien Bourdeauducq 52e331204e examples: fix first_dds_bus_channel in device database 2017-10-23 15:05:20 +08:00
Robert Jördens 8edb6a135a conda: openocd=0.10.0 (single-tap proxy bitstreams) 2017-10-20 19:47:54 +02:00
Robert Jördens cd0d73a1a2 scanwidget: protect against resize from zero
fix #839
2017-10-11 22:27:09 +02:00
Sebastien Bourdeauducq a6cd42c4aa RELEASE_NOTES: 2.5 2017-10-02 12:16:46 +08:00
whitequark 45c6ca96f8 firmware: unbreak heap view.
This was missing since 7799413a for no good reason.
2017-10-02 11:03:52 +08:00
whitequark db8300c990 compiler: disallow op= on mutable values (fix #835).
In general, we can't reallocate a value in earlier stack frames,
or fixup references to it. This mainly impacts lists.
2017-10-02 11:03:03 +08:00
whitequark ce7e30edfe compiler: implement ~ operator (fix #836). 2017-10-02 11:03:02 +08:00
whitequark a06f04dfbe compiler: minor intrinsic refactoring. 2017-10-02 11:03:02 +08:00
whitequark 1521231b1b compiler: correct semantics of floating point % operator (fix #830). 2017-10-02 11:03:02 +08:00
whitequark 67997d8955 compiler: correct semantics of integer % operator (#830). 2017-10-02 11:03:02 +08:00
Sebastien Bourdeauducq a49bb2bc50 conda: fix llvmlite-artiq dependency 2017-09-30 01:31:50 +08:00
Sebastien Bourdeauducq d500e61d89 RELEASE_NOTES: formatting 2017-09-30 01:10:23 +08:00
Sebastien Bourdeauducq 04a9a0ce95 doc: no more win32 packages 2017-09-29 23:14:21 +08:00
Sebastien Bourdeauducq ac28b377c7 targets: phaser → kc705_phaser 2017-09-29 22:54:18 +08:00
134 changed files with 3136 additions and 1973 deletions

View File

@ -95,4 +95,4 @@ then you just add a line saying
using your legal name (sorry, no pseudonyms or anonymous contributions.) using your legal name (sorry, no pseudonyms or anonymous contributions.)
ARTIQ files that do not contain a license header are copyrighted by M-Labs Limited ARTIQ files that do not contain a license header are copyrighted by M-Labs Limited
and are licensed under GNU GPL version 3. and are licensed under GNU LGPL version 3 or later.

View File

@ -9,11 +9,9 @@ It is maintained and developed by `M-Labs <https://m-labs.hk>`_ and the initial
The system features a high-level programming language that helps describing complex experiments, which is compiled and executed on dedicated hardware with nanosecond timing resolution and sub-microsecond latency. It includes graphical user interfaces to parametrize and schedule experiments and to visualize and explore the results. The system features a high-level programming language that helps describing complex experiments, which is compiled and executed on dedicated hardware with nanosecond timing resolution and sub-microsecond latency. It includes graphical user interfaces to parametrize and schedule experiments and to visualize and explore the results.
ARTIQ uses FPGA hardware to perform its time-critical tasks. ARTIQ uses FPGA hardware to perform its time-critical tasks. The `Sinara hardware <https://github.com/sinara-hw>`_, and in particular the Kasli FPGA carrier, is designed to work with ARTIQ (support for Sinara is available in ARTIQ-4 and above).
It is designed to be portable to hardware platforms from different vendors and FPGA manufacturers. ARTIQ is designed to be portable to hardware platforms from different vendors and FPGA manufacturers.
Currently, several different configurations of a `high-end FPGA evaluation kit <http://www.xilinx.com/products/boards-and-kits/ek-k7-kc705-g.html>`_ are used and supported. This FPGA platform can be combined with any number of additional peripherals, either already accessible from ARTIQ or made accessible with little effort. Several different configurations of a `high-end FPGA evaluation kit <http://www.xilinx.com/products/boards-and-kits/ek-k7-kc705-g.html>`_ are also used and supported. FPGA platforms can be combined with any number of additional peripherals, either already accessible from ARTIQ or made accessible with little effort.
Custom hardware components with widely extended capabilities and advanced support for scalable and fully distributed real-time control of experiments `are being designed <https://github.com/m-labs/sinara>`_.
ARTIQ and its dependencies are available in the form of `conda packages <https://conda.anaconda.org/m-labs/label/main>`_ for both Linux and Windows. ARTIQ and its dependencies are available in the form of `conda packages <https://conda.anaconda.org/m-labs/label/main>`_ for both Linux and Windows.
Packages containing pre-compiled binary images to be loaded onto the hardware platforms are supplied for each configuration. Packages containing pre-compiled binary images to be loaded onto the hardware platforms are supplied for each configuration.
@ -31,7 +29,7 @@ Website: https://m-labs.hk/artiq
License License
======= =======
Copyright (C) 2014-2017 M-Labs Limited. Copyright (C) 2014-2019 M-Labs Limited.
ARTIQ is free software: you can redistribute it and/or modify ARTIQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -3,13 +3,57 @@
Release notes Release notes
============= =============
3.7
---
No further notes.
3.6
---
No further notes.
3.5
---
No further notes.
3.4
---
No further notes.
3.3
---
No further notes.
3.2
---
* To accommodate larger runtimes, the flash layout as changed. As a result, the
contents of the flash storage will be lost when upgrading. Set the values back
(IP, MAC address, startup kernel, etc.) after the upgrade.
3.1
---
No further notes.
3.0 3.0
--- ---
* The --embed option of applets is replaced with the environment variable * The ``--embed`` option of applets is replaced with the environment variable
``ARTIQ_APPLET_EMBED``. The GUI sets this enviroment variable itself and the ``ARTIQ_APPLET_EMBED``. The GUI sets this enviroment variable itself and the
user simply needs to remove the ``--embed`` argument. user simply needs to remove the ``--embed`` argument.
* EnvExperiment's prepare calls prepare for all its children. * ``EnvExperiment``'s ``prepare`` calls ``prepare`` for all its children.
* Dynamic ``__getattr__``'s returning RPC target methods are not supported anymore. * Dynamic ``__getattr__``'s returning RPC target methods are not supported anymore.
Controller driver classes must define all their methods intended for RPC as Controller driver classes must define all their methods intended for RPC as
members. members.
@ -22,7 +66,7 @@ Release notes
* The DDS class names and setup options have changed, this requires an update of * The DDS class names and setup options have changed, this requires an update of
the device database. the device database.
* ``int(a, width=b)`` has been removed. Use ``int32(a)`` and ``int64(a)``. * ``int(a, width=b)`` has been removed. Use ``int32(a)`` and ``int64(a)``.
* The kc705 gateware target has been renamed kc705_dds. * The KC705 gateware target has been renamed ``kc705_dds``.
* ``artiq.coredevice.comm_tcp`` has been renamed ``artiq.coredevice.comm_kernel``, * ``artiq.coredevice.comm_tcp`` has been renamed ``artiq.coredevice.comm_kernel``,
and ``Comm`` has been renamed ``CommKernel``. and ``Comm`` has been renamed ``CommKernel``.
* The "collision" and "busy" RTIO errors are reported through the log instead of * The "collision" and "busy" RTIO errors are reported through the log instead of
@ -38,8 +82,8 @@ Release notes
identifiers (``true``, ``null``, ...) with their Python equivalents identifiers (``true``, ``null``, ...) with their Python equivalents
(``True``, ``None`` ...). (``True``, ``None`` ...).
* Controllers are now named ``aqctl_XXX`` instead of ``XXX_controller``. * Controllers are now named ``aqctl_XXX`` instead of ``XXX_controller``.
* In the device database, the "comm" device has been folded into the "core" device. * In the device database, the ``comm`` device has been folded into the ``core`` device.
Move the "host" argument into the "core" device, and remove the "comm" device. Move the "host" argument into the ``core`` device, and remove the ``comm`` device.
* The core device log now contains important information about events such as * The core device log now contains important information about events such as
RTIO collisions. A new controller ``aqctl_corelog`` must be running to forward RTIO collisions. A new controller ``aqctl_corelog`` must be running to forward
those logs to the master. See the example device databases to see how to those logs to the master. See the example device databases to see how to
@ -52,8 +96,15 @@ Release notes
at https://github.com/m-labs/pdq. All SPI/USB driver layers, Mediator, at https://github.com/m-labs/pdq. All SPI/USB driver layers, Mediator,
CompoundPDQ and examples/documentation has been moved. CompoundPDQ and examples/documentation has been moved.
* The master now rotates log files at midnight, rather than based on log size. * The master now rotates log files at midnight, rather than based on log size.
* The results keys start_time and run_time are now stored as doubles of UNIX time, * The results keys ``start_time`` and ``run_time`` are now stored as doubles of UNIX time,
rather than ints. The file names are still based on local time. rather than ints. The file names are still based on local time.
* Packages are no longer available for 32-bit Windows.
2.5
---
No further notes.
2.4 2.4
@ -62,7 +113,6 @@ Release notes
No further notes. No further notes.
2.3 2.3
--- ---

View File

@ -258,8 +258,9 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
def dropEvent(self, ev): def dropEvent(self, ev):
for uri in ev.mimeData().urls(): for uri in ev.mimeData().urls():
if uri.scheme() == "file": if uri.scheme() == "file":
logger.debug("Loading HDF5 arguments from %s", uri.path()) filename = QtCore.QDir.toNativeSeparators(uri.toLocalFile())
asyncio.ensure_future(self.load_hdf5_task(uri.path())) logger.debug("Loading HDF5 arguments from %s", filename)
asyncio.ensure_future(self.load_hdf5_task(filename))
break break
async def compute_arginfo(self): async def compute_arginfo(self):

View File

@ -169,6 +169,9 @@ def fn_ValueError():
def fn_ZeroDivisionError(): def fn_ZeroDivisionError():
return types.TExceptionConstructor(TException("ZeroDivisionError")) return types.TExceptionConstructor(TException("ZeroDivisionError"))
def fn_RuntimeError():
return types.TExceptionConstructor(TException("RuntimeError"))
def fn_range(): def fn_range():
return types.TBuiltinFunction("range") return types.TBuiltinFunction("range")

View File

@ -749,6 +749,11 @@ class Stitcher:
quote_function=self._quote_function) quote_function=self._quote_function)
def _function_loc(self, function): def _function_loc(self, function):
if isinstance(function, SpecializedFunction):
function = function.host_function
if hasattr(function, 'artiq_embedded') and function.artiq_embedded.function:
function = function.artiq_embedded.function
filename = function.__code__.co_filename filename = function.__code__.co_filename
line = function.__code__.co_firstlineno line = function.__code__.co_firstlineno
name = function.__code__.co_name name = function.__code__.co_name

View File

@ -64,8 +64,8 @@ class Module:
interleaver = transforms.Interleaver(engine=self.engine) interleaver = transforms.Interleaver(engine=self.engine)
invariant_detection = analyses.InvariantDetection(engine=self.engine) invariant_detection = analyses.InvariantDetection(engine=self.engine)
cast_monomorphizer.visit(src.typedtree)
int_monomorphizer.visit(src.typedtree) int_monomorphizer.visit(src.typedtree)
cast_monomorphizer.visit(src.typedtree)
inferencer.visit(src.typedtree) inferencer.visit(src.typedtree)
monomorphism_validator.visit(src.typedtree) monomorphism_validator.visit(src.typedtree)
escape_validator.visit(src.typedtree) escape_validator.visit(src.typedtree)

View File

@ -1,4 +1,4 @@
import os, sys, tempfile, subprocess import os, sys, tempfile, subprocess, io
from artiq.compiler import types from artiq.compiler import types
from llvmlite_artiq import ir as ll, binding as llvm from llvmlite_artiq import ir as ll, binding as llvm
@ -8,40 +8,44 @@ llvm.initialize_all_asmprinters()
class RunTool: class RunTool:
def __init__(self, pattern, **tempdata): def __init__(self, pattern, **tempdata):
self.files = [] self._pattern = pattern
self.pattern = pattern self._tempdata = tempdata
self.tempdata = tempdata self._tempnames = {}
self._tempfiles = {}
def maketemp(self, data):
f = tempfile.NamedTemporaryFile()
f.write(data)
f.flush()
self.files.append(f)
return f
def __enter__(self): def __enter__(self):
tempfiles = {} for key, data in self._tempdata.items():
tempnames = {} if data is None:
for key in self.tempdata: fd, filename = tempfile.mkstemp()
tempfiles[key] = self.maketemp(self.tempdata[key]) os.close(fd)
tempnames[key] = tempfiles[key].name self._tempnames[key] = filename
else:
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(data)
self._tempnames[key] = f.name
cmdline = [] cmdline = []
for argument in self.pattern: for argument in self._pattern:
cmdline.append(argument.format(**tempnames)) cmdline.append(argument.format(**self._tempnames))
process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
stdout, stderr = process.communicate() stdout, stderr = process.communicate()
if process.returncode != 0: if process.returncode != 0:
raise Exception("{} invocation failed: {}". raise Exception("{} invocation failed: {}".
format(cmdline[0], stderr.decode('utf-8'))) format(cmdline[0], stderr))
tempfiles["__stdout__"] = stdout.decode('utf-8') self._tempfiles["__stdout__"] = io.StringIO(stdout)
return tempfiles for key in self._tempdata:
if self._tempdata[key] is None:
self._tempfiles[key] = open(self._tempnames[key], "rb")
return self._tempfiles
def __exit__(self, exc_typ, exc_value, exc_trace): def __exit__(self, exc_typ, exc_value, exc_trace):
for f in self.files: for file in self._tempfiles.values():
f.close() file.close()
for filename in self._tempnames.values():
os.unlink(filename)
def _dump(target, kind, suffix, content): def _dump(target, kind, suffix, content):
if target is not None: if target is not None:
@ -166,7 +170,7 @@ class Target:
with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] + with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] +
["{{obj{}}}".format(index) for index in range(len(objects))] + ["{{obj{}}}".format(index) for index in range(len(objects))] +
["-o", "{output}"], ["-o", "{output}"],
output=b"", output=None,
**{"obj{}".format(index): obj for index, obj in enumerate(objects)}) \ **{"obj{}".format(index): obj for index, obj in enumerate(objects)}) \
as results: as results:
library = results["output"].read() library = results["output"].read()
@ -181,7 +185,7 @@ class Target:
def strip(self, library): def strip(self, library):
with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"], with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"],
library=library, output=b"") \ library=library, output=None) \
as results: as results:
return results["output"].read() return results["output"].read()
@ -198,7 +202,7 @@ class Target:
"--demangle", "--exe={library}"] + offset_addresses, "--demangle", "--exe={library}"] + offset_addresses,
library=library) \ library=library) \
as results: as results:
lines = iter(results["__stdout__"].rstrip().split("\n")) lines = iter(results["__stdout__"].read().rstrip().split("\n"))
backtrace = [] backtrace = []
while True: while True:
try: try:
@ -216,13 +220,17 @@ class Target:
filename, line = location.rsplit(":", 1) filename, line = location.rsplit(":", 1)
if filename == "??" or filename == "<synthesized>": if filename == "??" or filename == "<synthesized>":
continue continue
if line == "?":
line = -1
else:
line = int(line)
# can't get column out of addr2line D: # can't get column out of addr2line D:
backtrace.append((filename, int(line), -1, function, address)) backtrace.append((filename, line, -1, function, address))
return backtrace return backtrace
def demangle(self, names): def demangle(self, names):
with RunTool([self.triple + "-c++filt"] + names) as results: with RunTool([self.triple + "-c++filt"] + names) as results:
return results["__stdout__"].rstrip().split("\n") return results["__stdout__"].read().rstrip().split("\n")
class NativeTarget(Target): class NativeTarget(Target):
def __init__(self): def __init__(self):

View File

@ -1,4 +1,4 @@
import sys, os import sys, os, tokenize
from artiq.master.databases import DeviceDB from artiq.master.databases import DeviceDB
from artiq.master.worker_db import DeviceManager from artiq.master.worker_db import DeviceManager
@ -27,7 +27,7 @@ def main():
ddb_path = os.path.join(os.path.dirname(sys.argv[1]), "device_db.py") ddb_path = os.path.join(os.path.dirname(sys.argv[1]), "device_db.py")
dmgr = DeviceManager(DeviceDB(ddb_path)) dmgr = DeviceManager(DeviceDB(ddb_path))
with open(sys.argv[1]) as f: with tokenize.open(sys.argv[1]) as f:
testcase_code = compile(f.read(), f.name, "exec") testcase_code = compile(f.read(), f.name, "exec")
testcase_vars = {'__name__': 'testbench', 'dmgr': dmgr} testcase_vars = {'__name__': 'testbench', 'dmgr': dmgr}
exec(testcase_code, testcase_vars) exec(testcase_code, testcase_vars)

View File

@ -1,4 +1,4 @@
import sys, os import sys, os, tokenize
from pythonparser import diagnostic from pythonparser import diagnostic
from ...language.environment import ProcessArgumentManager from ...language.environment import ProcessArgumentManager
from ...master.databases import DeviceDB, DatasetDB from ...master.databases import DeviceDB, DatasetDB
@ -22,7 +22,7 @@ def main():
engine = diagnostic.Engine() engine = diagnostic.Engine()
engine.process = process_diagnostic engine.process = process_diagnostic
with open(sys.argv[1]) as f: with tokenize.open(sys.argv[1]) as f:
testcase_code = compile(f.read(), f.name, "exec") testcase_code = compile(f.read(), f.name, "exec")
testcase_vars = {'__name__': 'testbench'} testcase_vars = {'__name__': 'testbench'}
exec(testcase_code, testcase_vars) exec(testcase_code, testcase_vars)

View File

@ -5,6 +5,6 @@ from .cast_monomorphizer import CastMonomorphizer
from .iodelay_estimator import IODelayEstimator from .iodelay_estimator import IODelayEstimator
from .artiq_ir_generator import ARTIQIRGenerator from .artiq_ir_generator import ARTIQIRGenerator
from .dead_code_eliminator import DeadCodeEliminator from .dead_code_eliminator import DeadCodeEliminator
from .llvm_ir_generator import LLVMIRGenerator
from .interleaver import Interleaver from .interleaver import Interleaver
from .typedtree_printer import TypedtreePrinter from .typedtree_printer import TypedtreePrinter
from .llvm_ir_generator import LLVMIRGenerator

View File

@ -1289,6 +1289,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
return self.append(ir.Select(cond, return self.append(ir.Select(cond,
ir.Constant(False, builtins.TBool()), ir.Constant(False, builtins.TBool()),
ir.Constant(True, builtins.TBool()))) ir.Constant(True, builtins.TBool())))
elif isinstance(node.op, ast.Invert):
operand = self.visit(node.operand)
return self.append(ir.Arith(ast.BitXor(loc=None),
ir.Constant(-1, operand.type), operand))
elif isinstance(node.op, ast.USub): elif isinstance(node.op, ast.USub):
operand = self.visit(node.operand) operand = self.visit(node.operand)
return self.append(ir.Arith(ast.Sub(loc=None), return self.append(ir.Arith(ast.Sub(loc=None),
@ -1319,7 +1323,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
lambda: self.alloc_exn(builtins.TException("ValueError"), lambda: self.alloc_exn(builtins.TException("ValueError"),
ir.Constant("shift amount must be nonnegative", builtins.TStr())), ir.Constant("shift amount must be nonnegative", builtins.TStr())),
loc=node.right.loc) loc=node.right.loc)
elif isinstance(node.op, (ast.Div, ast.FloorDiv)): elif isinstance(node.op, (ast.Div, ast.FloorDiv, ast.Mod)):
self._make_check( self._make_check(
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))), self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
lambda: self.alloc_exn(builtins.TException("ZeroDivisionError"), lambda: self.alloc_exn(builtins.TException("ZeroDivisionError"),
@ -1474,7 +1478,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
else: else:
assert False assert False
def polymorphic_compare_pair_inclusion(self, op, needle, haystack): def polymorphic_compare_pair_inclusion(self, needle, haystack):
if builtins.is_range(haystack.type): if builtins.is_range(haystack.type):
# Optimized range `in` operator # Optimized range `in` operator
start = self.append(ir.GetAttr(haystack, "start")) start = self.append(ir.GetAttr(haystack, "start"))
@ -1518,21 +1522,29 @@ class ARTIQIRGenerator(algorithm.Visitor):
else: else:
assert False assert False
if isinstance(op, ast.NotIn):
result = self.append(ir.Select(result,
ir.Constant(False, builtins.TBool()),
ir.Constant(True, builtins.TBool())))
return result return result
def invert(self, value):
return self.append(ir.Select(value,
ir.Constant(False, builtins.TBool()),
ir.Constant(True, builtins.TBool())))
def polymorphic_compare_pair(self, op, lhs, rhs): def polymorphic_compare_pair(self, op, lhs, rhs):
if isinstance(op, (ast.Is, ast.IsNot)): if isinstance(op, (ast.Is, ast.IsNot)):
# The backend will handle equality of aggregates. # The backend will handle equality of aggregates.
return self.append(ir.Compare(op, lhs, rhs)) return self.append(ir.Compare(op, lhs, rhs))
elif isinstance(op, (ast.In, ast.NotIn)): elif isinstance(op, ast.In):
return self.polymorphic_compare_pair_inclusion(op, lhs, rhs) return self.polymorphic_compare_pair_inclusion(lhs, rhs)
else: # Eq, NotEq, Lt, LtE, Gt, GtE elif isinstance(op, ast.NotIn):
result = self.polymorphic_compare_pair_inclusion(lhs, rhs)
return self.invert(result)
elif isinstance(op, (ast.Eq, ast.Lt, ast.LtE, ast.Gt, ast.GtE)):
return self.polymorphic_compare_pair_order(op, lhs, rhs) return self.polymorphic_compare_pair_order(op, lhs, rhs)
elif isinstance(op, ast.NotEq):
result = self.polymorphic_compare_pair_order(ast.Eq(loc=op.loc), lhs, rhs)
return self.invert(result)
else:
assert False
def visit_CompareT(self, node): def visit_CompareT(self, node):
# Essentially a sequence of `and`s performed over results # Essentially a sequence of `and`s performed over results
@ -1622,7 +1634,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
else: else:
assert False assert False
elif (types.is_builtin(typ, "list") or types.is_builtin(typ, "array") or elif (types.is_builtin(typ, "list") or types.is_builtin(typ, "array") or
types.is_builtin(typ, "bytearray")): types.is_builtin(typ, "bytearray") or types.is_builtin(typ, "bytes")):
if len(node.args) == 0 and len(node.keywords) == 0: if len(node.args) == 0 and len(node.keywords) == 0:
length = ir.Constant(0, builtins.TInt32()) length = ir.Constant(0, builtins.TInt32())
return self.append(ir.Alloc([length], node.type)) return self.append(ir.Alloc([length], node.type))
@ -1633,6 +1645,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
def body_gen(index): def body_gen(index):
elt = self.iterable_get(arg, index) elt = self.iterable_get(arg, index)
elt = self.append(ir.Coerce(elt, builtins.get_iterable_elt(node.type)))
self.append(ir.SetElem(result, index, elt)) self.append(ir.SetElem(result, index, elt))
return self.append(ir.Arith(ast.Add(loc=None), index, return self.append(ir.Arith(ast.Add(loc=None), index,
ir.Constant(1, length.type))) ir.Constant(1, length.type)))
@ -1721,7 +1734,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.polymorphic_print([self.visit(prefix)], self.polymorphic_print([self.visit(prefix)],
separator=" ", suffix="\x1E", as_rtio=True) separator=" ", suffix="\x1E", as_rtio=True)
self.polymorphic_print([self.visit(arg) for arg in args], self.polymorphic_print([self.visit(arg) for arg in args],
separator=" ", suffix="\n\x1D", as_rtio=True) separator=" ", suffix="\x1D", as_rtio=True)
return ir.Constant(None, builtins.TNone()) return ir.Constant(None, builtins.TNone())
elif types.is_builtin(typ, "delay"): elif types.is_builtin(typ, "delay"):
if len(node.args) == 1 and len(node.keywords) == 0: if len(node.args) == 1 and len(node.keywords) == 0:
@ -1808,7 +1821,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
assert None not in args assert None not in args
if self.unwind_target is None: if self.unwind_target is None or \
types.is_c_function(callee.type) and "nounwind" in callee.type.flags:
insn = self.append(ir.Call(func, args, arg_exprs)) insn = self.append(ir.Call(func, args, arg_exprs))
else: else:
after_invoke = self.add_block("invoke") after_invoke = self.add_block("invoke")

View File

@ -11,13 +11,12 @@ class CastMonomorphizer(algorithm.Visitor):
self.engine = engine self.engine = engine
def visit_CallT(self, node): def visit_CallT(self, node):
self.generic_visit(node)
if (types.is_builtin(node.func.type, "int") or if (types.is_builtin(node.func.type, "int") or
types.is_builtin(node.func.type, "int32") or types.is_builtin(node.func.type, "int32") or
types.is_builtin(node.func.type, "int64")): types.is_builtin(node.func.type, "int64")):
typ = node.type.find() typ = node.type.find()
if (not types.is_var(typ["width"]) and if (not types.is_var(typ["width"]) and
len(node.args) == 1 and
builtins.is_int(node.args[0].type) and builtins.is_int(node.args[0].type) and
types.is_var(node.args[0].type.find()["width"])): types.is_var(node.args[0].type.find()["width"])):
if isinstance(node.args[0], asttyped.BinOpT): if isinstance(node.args[0], asttyped.BinOpT):
@ -29,3 +28,20 @@ class CastMonomorphizer(algorithm.Visitor):
node.args[0].type.unify(typ) node.args[0].type.unify(typ)
if types.is_builtin(node.func.type, "int") or \
types.is_builtin(node.func.type, "round"):
typ = node.type.find()
if types.is_var(typ["width"]):
typ["width"].unify(types.TValue(32))
self.generic_visit(node)
def visit_CoerceT(self, node):
if isinstance(node.value, asttyped.NumT) and \
builtins.is_int(node.type) and \
builtins.is_int(node.value.type) and \
not types.is_var(node.type["width"]) and \
types.is_var(node.value.type["width"]):
node.value.type.unify(node.type)
self.generic_visit(node)

View File

@ -310,7 +310,15 @@ class Inferencer(algorithm.Visitor):
node_types = [] node_types = []
for node in nodes: for node in nodes:
if isinstance(node, asttyped.CoerceT): if isinstance(node, asttyped.CoerceT):
node_types.append(node.value.type) # If we already know exactly what we coerce this value to, use that type,
# or we'll get an unification error in case the coerced type is not the same
# as the type of the coerced value.
# Otherwise, use the potentially more specific subtype when considering possible
# coercions, or we may get stuck.
if node.type.fold(False, lambda acc, ty: acc or types.is_var(ty)):
node_types.append(node.value.type)
else:
node_types.append(node.type)
else: else:
node_types.append(node.type) node_types.append(node.type)
if any(map(types.is_var, node_types)): # not enough info yet if any(map(types.is_var, node_types)): # not enough info yet
@ -682,6 +690,11 @@ class Inferencer(algorithm.Visitor):
pass pass
else: else:
diagnose(valid_forms()) diagnose(valid_forms())
elif types.is_builtin(typ, "str"):
diag = diagnostic.Diagnostic("error",
"strings currently cannot be constructed", {},
node.loc)
self.engine.process(diag)
elif types.is_builtin(typ, "list") or types.is_builtin(typ, "array"): elif types.is_builtin(typ, "list") or types.is_builtin(typ, "array"):
if types.is_builtin(typ, "list"): if types.is_builtin(typ, "list"):
valid_forms = lambda: [ valid_forms = lambda: [
@ -999,6 +1012,17 @@ class Inferencer(algorithm.Visitor):
elif keyword.arg in typ_optargs: elif keyword.arg in typ_optargs:
self._unify(keyword.value.type, typ_optargs[keyword.arg], self._unify(keyword.value.type, typ_optargs[keyword.arg],
keyword.value.loc, None) keyword.value.loc, None)
else:
note = diagnostic.Diagnostic("note",
"extraneous argument", {},
keyword.loc)
diag = diagnostic.Diagnostic("error",
"this function of type {type} does not accept argument '{name}'",
{"type": types.TypePrinter().name(node.func.type),
"name": keyword.arg},
node.func.loc, [], [note])
self.engine.process(diag)
return
passed_args[keyword.arg] = keyword.arg_loc passed_args[keyword.arg] = keyword.arg_loc
for formalname in typ_args: for formalname in typ_args:

View File

@ -26,22 +26,3 @@ class IntMonomorphizer(algorithm.Visitor):
return return
node.type["width"].unify(types.TValue(width)) node.type["width"].unify(types.TValue(width))
def visit_CallT(self, node):
self.generic_visit(node)
if types.is_builtin(node.func.type, "int") or \
types.is_builtin(node.func.type, "round"):
typ = node.type.find()
if types.is_var(typ["width"]):
typ["width"].unify(types.TValue(32))
def visit_CoerceT(self, node):
if isinstance(node.value, asttyped.NumT) and \
builtins.is_int(node.type) and \
builtins.is_int(node.value.type) and \
not types.is_var(node.type["width"]) and \
types.is_var(node.value.type["width"]):
node.value.type.unify(node.type)
self.generic_visit(node)

View File

@ -9,6 +9,7 @@ from pythonparser import ast, diagnostic
from llvmlite_artiq import ir as ll, binding as llvm from llvmlite_artiq import ir as ll, binding as llvm
from ...language import core as language_core from ...language import core as language_core
from .. import types, builtins, ir from .. import types, builtins, ir
from ..embedding import SpecializedFunction
llvoid = ll.VoidType() llvoid = ll.VoidType()
@ -22,7 +23,7 @@ llptr = ll.IntType(8).as_pointer()
llptrptr = ll.IntType(8).as_pointer().as_pointer() llptrptr = ll.IntType(8).as_pointer().as_pointer()
llslice = ll.LiteralStructType([llptr, lli32]) llslice = ll.LiteralStructType([llptr, lli32])
llsliceptr = ll.LiteralStructType([llptr, lli32]).as_pointer() llsliceptr = ll.LiteralStructType([llptr, lli32]).as_pointer()
llmetadata = ll.MetaData() llmetadata = ll.MetaDataType()
def memoize(generator): def memoize(generator):
@ -270,7 +271,7 @@ class LLVMIRGenerator:
sanitized_str = re.sub(rb"[^a-zA-Z0-9_.]", b"", as_bytes[:20]).decode('ascii') sanitized_str = re.sub(rb"[^a-zA-Z0-9_.]", b"", as_bytes[:20]).decode('ascii')
name = self.llmodule.get_unique_name("S.{}".format(sanitized_str)) name = self.llmodule.get_unique_name("S.{}".format(sanitized_str))
llstr = self.llmodule.get_global(name) llstr = self.llmodule.globals.get(name)
if llstr is None: if llstr is None:
llstrty = ll.ArrayType(lli8, len(as_bytes)) llstrty = ll.ArrayType(lli8, len(as_bytes))
llstr = ll.GlobalVariable(self.llmodule, llstrty, name) llstr = ll.GlobalVariable(self.llmodule, llstrty, name)
@ -306,7 +307,7 @@ class LLVMIRGenerator:
assert False assert False
def llbuiltin(self, name): def llbuiltin(self, name):
llglobal = self.llmodule.get_global(name) llglobal = self.llmodule.globals.get(name)
if llglobal is not None: if llglobal is not None:
return llglobal return llglobal
@ -326,6 +327,12 @@ class LLVMIRGenerator:
llty = ll.FunctionType(llptr, []) llty = ll.FunctionType(llptr, [])
elif name == "llvm.stackrestore": elif name == "llvm.stackrestore":
llty = ll.FunctionType(llvoid, [llptr]) llty = ll.FunctionType(llvoid, [llptr])
elif name == "__py_modsi3":
llty = ll.FunctionType(lli32, [lli32, lli32])
elif name == "__py_moddi3":
llty = ll.FunctionType(lli64, [lli64, lli64])
elif name == "__py_moddf3":
llty = ll.FunctionType(lldouble, [lldouble, lldouble])
elif name == self.target.print_function: elif name == self.target.print_function:
llty = ll.FunctionType(llvoid, [llptr], var_arg=True) llty = ll.FunctionType(llvoid, [llptr], var_arg=True)
elif name == "rtio_log": elif name == "rtio_log":
@ -363,13 +370,96 @@ class LLVMIRGenerator:
"watchdog_set", "watchdog_clear", "watchdog_set", "watchdog_clear",
self.target.print_function): self.target.print_function):
llglobal.attributes.add("nounwind") llglobal.attributes.add("nounwind")
if name.find("__py_") == 0:
llglobal.linkage = 'linkonce_odr'
self.emit_intrinsic(name, llglobal)
else: else:
llglobal = ll.GlobalVariable(self.llmodule, llty, name) llglobal = ll.GlobalVariable(self.llmodule, llty, name)
return llglobal return llglobal
def emit_intrinsic(self, name, llfun):
llbuilder = ll.IRBuilder()
llbuilder.position_at_end(llfun.append_basic_block("entry"))
if name == "__py_modsi3" or name == "__py_moddi3":
if name == "__py_modsi3":
llty = lli32
elif name == "__py_moddi3":
llty = lli64
else:
assert False
"""
Reference Objects/intobject.c
xdivy = x / y;
xmody = (long)(x - (unsigned long)xdivy * y);
/* If the signs of x and y differ, and the remainder is non-0,
* C89 doesn't define whether xdivy is now the floor or the
* ceiling of the infinitely precise quotient. We want the floor,
* and we have it iff the remainder's sign matches y's.
*/
if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) {
xmody += y;
// ...
}
"""
llx, lly = llfun.args
llxdivy = llbuilder.sdiv(llx, lly)
llxremy = llbuilder.srem(llx, lly)
llxmodynonzero = llbuilder.icmp_signed('!=', llxremy, ll.Constant(llty, 0))
lldiffsign = llbuilder.icmp_signed('<', llbuilder.xor(llx, lly), ll.Constant(llty, 0))
llcond = llbuilder.and_(llxmodynonzero, lldiffsign)
with llbuilder.if_then(llcond):
llbuilder.ret(llbuilder.add(llxremy, lly))
llbuilder.ret(llxremy)
elif name == "__py_moddf3":
"""
Reference Objects/floatobject.c
mod = fmod(vx, wx);
/* fmod is typically exact, so vx-mod is *mathematically* an
exact multiple of wx. But this is fp arithmetic, and fp
vx - mod is an approximation; the result is that div may
not be an exact integral value after the division, although
it will always be very close to one.
*/
// ...
if (mod) {
/* ensure the remainder has the same sign as the denominator */
if ((wx < 0) != (mod < 0)) {
mod += wx;
// ...
}
}
else {
/* the remainder is zero, and in the presence of signed zeroes
fmod returns different results across platforms; ensure
it has the same sign as the denominator; we'd like to do
"mod = wx * 0.0", but that may get optimized away */
mod *= mod; /* hide "mod = +0" from optimizer */
if (wx < 0.0)
mod = -mod;
}
"""
llv, llw = llfun.args
llrem = llbuilder.frem(llv, llw)
llremnonzero = llbuilder.fcmp_unordered('!=', llrem, ll.Constant(lldouble, 0.0))
llwltzero = llbuilder.fcmp_ordered('<', llw, ll.Constant(lldouble, 0.0))
llremltzero = llbuilder.fcmp_ordered('<', llrem, ll.Constant(lldouble, 0.0))
lldiffsign = llbuilder.icmp_unsigned('!=', llwltzero, llremltzero)
llcond = llbuilder.and_(llremnonzero, lldiffsign)
with llbuilder.if_then(llcond):
llbuilder.ret(llbuilder.fadd(llrem, llw))
llbuilder.ret(llrem)
else:
assert False
def get_function(self, typ, name): def get_function(self, typ, name):
llfun = self.llmodule.get_global(name) llfun = self.llmodule.globals.get(name)
if llfun is None: if llfun is None:
llfunty = self.llty_of_type(typ, bare=True) llfunty = self.llty_of_type(typ, bare=True)
llfun = ll.Function(self.llmodule, llfunty, name) llfun = ll.Function(self.llmodule, llfunty, name)
@ -410,7 +500,7 @@ class LLVMIRGenerator:
llobjects = defaultdict(lambda: []) llobjects = defaultdict(lambda: [])
for obj_id, obj_ref, obj_typ in self.embedding_map.iter_objects(): for obj_id, obj_ref, obj_typ in self.embedding_map.iter_objects():
llobject = self.llmodule.get_global("O.{}".format(obj_id)) llobject = self.llmodule.globals.get("O.{}".format(obj_id))
if llobject is not None: if llobject is not None:
llobjects[obj_typ].append(llobject.bitcast(llptr)) llobjects[obj_typ].append(llobject.bitcast(llptr))
@ -435,11 +525,10 @@ class LLVMIRGenerator:
print(typ) print(typ)
assert False assert False
if not (types.is_function(typ) or types.is_method(typ) or types.is_rpc(typ) or if name == "__objectid__":
name == "__objectid__"):
rpctag = b"Os" + self._rpc_tag(typ, error_handler=rpc_tag_error) + b":n"
else:
rpctag = b"" rpctag = b""
else:
rpctag = b"Os" + self._rpc_tag(typ, error_handler=rpc_tag_error) + b":n"
llrpcattrinit = ll.Constant(llrpcattrty, [ llrpcattrinit = ll.Constant(llrpcattrty, [
ll.Constant(lli32, offset), ll.Constant(lli32, offset),
@ -472,7 +561,10 @@ class LLVMIRGenerator:
offset += alignment - (offset % alignment) offset += alignment - (offset % alignment)
if types.is_instance(typ) and attr not in typ.constant_attributes: if types.is_instance(typ) and attr not in typ.constant_attributes:
llrpcattrs.append(llrpcattr_of_attr(offset, attr, attrtyp)) try:
llrpcattrs.append(llrpcattr_of_attr(offset, attr, attrtyp))
except ValueError:
continue
offset += size offset += size
@ -905,22 +997,15 @@ class LLVMIRGenerator:
return self.llbuilder.sdiv(self.map(insn.lhs()), self.map(insn.rhs()), return self.llbuilder.sdiv(self.map(insn.lhs()), self.map(insn.rhs()),
name=insn.name) name=insn.name)
elif isinstance(insn.op, ast.Mod): elif isinstance(insn.op, ast.Mod):
# Python only has the modulo operator, LLVM only has the remainder lllhs, llrhs = map(self.map, (insn.lhs(), insn.rhs()))
if builtins.is_float(insn.type): if builtins.is_float(insn.type):
llvalue = self.llbuilder.frem(self.map(insn.lhs()), self.map(insn.rhs())) intrinsic = "__py_moddf3"
self.add_fast_math_flags(llvalue) elif builtins.is_int32(insn.type):
return self.llbuilder.call(self.llbuiltin("llvm.copysign.f64"), intrinsic = "__py_modsi3"
[llvalue, self.map(insn.rhs())], elif builtins.is_int64(insn.type):
name=insn.name) intrinsic = "__py_moddi3"
else: return self.llbuilder.call(self.llbuiltin(intrinsic), [lllhs, llrhs],
lllhs, llrhs = map(self.map, (insn.lhs(), insn.rhs())) name=insn.name)
llxorsign = self.llbuilder.and_(self.llbuilder.xor(lllhs, llrhs),
ll.Constant(lllhs.type, 1 << lllhs.type.width - 1))
llnegate = self.llbuilder.icmp_unsigned('!=',
llxorsign, ll.Constant(llxorsign.type, 0))
llvalue = self.llbuilder.srem(lllhs, llrhs)
llnegvalue = self.llbuilder.sub(ll.Constant(llvalue.type, 0), llvalue)
return self.llbuilder.select(llnegate, llnegvalue, llvalue)
elif isinstance(insn.op, ast.Pow): elif isinstance(insn.op, ast.Pow):
if builtins.is_float(insn.type): if builtins.is_float(insn.type):
return self.llbuilder.call(self.llbuiltin("llvm.pow.f64"), return self.llbuilder.call(self.llbuiltin("llvm.pow.f64"),
@ -1127,7 +1212,7 @@ class LLVMIRGenerator:
llargs.append(llarg) llargs.append(llarg)
llfunname = insn.target_function().type.name llfunname = insn.target_function().type.name
llfun = self.llmodule.get_global(llfunname) llfun = self.llmodule.globals.get(llfunname)
if llfun is None: if llfun is None:
llretty = self.llty_of_type(insn.type, for_return=True) llretty = self.llty_of_type(insn.type, for_return=True)
if self.needs_sret(llretty): if self.needs_sret(llretty):
@ -1184,6 +1269,8 @@ class LLVMIRGenerator:
elif ir.is_keyword(typ): elif ir.is_keyword(typ):
return b"k" + self._rpc_tag(typ.params["value"], return b"k" + self._rpc_tag(typ.params["value"],
error_handler) error_handler)
elif types.is_function(typ) or types.is_method(typ) or types.is_rpc(typ):
raise ValueError("RPC tag for functional value")
elif '__objectid__' in typ.attributes: elif '__objectid__' in typ.attributes:
return b"O" return b"O"
else: else:
@ -1256,6 +1343,12 @@ class LLVMIRGenerator:
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
if fun_type.async: if fun_type.async:
# If this RPC is called using an `invoke` ARTIQ IR instruction, there will be
# no other instructions in this basic block. Since this RPC is async, it cannot
# possibly raise an exception, so add an explicit jump to the normal successor.
if llunwindblock:
self.llbuilder.branch(llnormalblock)
return ll.Undefined return ll.Undefined
# T result = { # T result = {
@ -1366,9 +1459,8 @@ class LLVMIRGenerator:
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock, llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
name=insn.name) name=insn.name)
# See the comment in process_Call. # The !tbaa metadata is not legal to use with the invoke instruction,
if types.is_c_function(functiontyp) and 'nowrite' in functiontyp.flags: # so unlike process_Call, we do not set it here.
llcall.set_metadata('tbaa', self.tbaa_nowrite_call)
return llcall return llcall
@ -1464,12 +1556,20 @@ class LLVMIRGenerator:
lleltsptr = llglobal.bitcast(lleltsary.type.element.as_pointer()) lleltsptr = llglobal.bitcast(lleltsary.type.element.as_pointer())
llconst = ll.Constant(llty, [lleltsptr, ll.Constant(lli32, len(llelts))]) llconst = ll.Constant(llty, [lleltsptr, ll.Constant(lli32, len(llelts))])
return llconst return llconst
elif types.is_rpc(typ) or types.is_c_function(typ): elif types.is_rpc(typ) or types.is_c_function(typ) or types.is_builtin_function(typ):
# RPC and C functions have no runtime representation. # RPC, C and builtin functions have no runtime representation.
return ll.Constant(llty, ll.Undefined) return ll.Constant(llty, ll.Undefined)
elif types.is_function(typ): elif types.is_function(typ):
return self.get_function_with_undef_env(typ.find(), try:
self.embedding_map.retrieve_function(value)) func = self.embedding_map.retrieve_function(value)
except KeyError:
# If a class function was embedded directly (e.g. by a `C.f(...)` call),
# but it also appears in a class hierarchy, we might need to fall back
# to the non-specialized one, since direct invocations do not cause
# monomorphization.
assert isinstance(value, SpecializedFunction)
func = self.embedding_map.retrieve_function(value.host_function)
return self.get_function_with_undef_env(typ.find(), func)
elif types.is_method(typ): elif types.is_method(typ):
llclosure = self._quote(value.__func__, types.get_method_function(typ), llclosure = self._quote(value.__func__, types.get_method_function(typ),
lambda: path() + ['__func__']) lambda: path() + ['__func__'])
@ -1561,7 +1661,7 @@ class LLVMIRGenerator:
llclauseexnname = self.llconst_of_const( llclauseexnname = self.llconst_of_const(
ir.Constant(exnname, builtins.TStr())) ir.Constant(exnname, builtins.TStr()))
llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname)) llclauseexnnameptr = self.llmodule.globals.get("exn.{}".format(exnname))
if llclauseexnnameptr is None: if llclauseexnnameptr is None:
llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type, llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type,
name="exn.{}".format(exnname)) name="exn.{}".format(exnname))

View File

@ -605,6 +605,14 @@ def is_builtin(typ, name=None):
return isinstance(typ, TBuiltin) and \ return isinstance(typ, TBuiltin) and \
typ.name == name typ.name == name
def is_builtin_function(typ, name=None):
typ = typ.find()
if name is None:
return isinstance(typ, TBuiltinFunction)
else:
return isinstance(typ, TBuiltinFunction) and \
typ.name == name
def is_constructor(typ, name=None): def is_constructor(typ, name=None):
typ = typ.find() typ = typ.find()
if name is not None: if name is not None:

View File

@ -298,8 +298,8 @@ class EscapeValidator(algorithm.Visitor):
# and exceptions can only refer to strings, so we don't actually check # and exceptions can only refer to strings, so we don't actually check
# this property. But we will need to, if string operations are ever added. # this property. But we will need to, if string operations are ever added.
def visit_assignment(self, target, value, is_aug_assign=False): def visit_assignment(self, target, value):
value_region = self._region_of(value) if not is_aug_assign else self.youngest_region value_region = self._region_of(value)
# If this is a variable, we might need to contract the live range. # If this is a variable, we might need to contract the live range.
if isinstance(value_region, Region): if isinstance(value_region, Region):
@ -316,19 +316,11 @@ class EscapeValidator(algorithm.Visitor):
target_regions = [self._region_of(name) for name in target_names] target_regions = [self._region_of(name) for name in target_names]
for target_region in target_regions: for target_region in target_regions:
if not Region.outlives(value_region, target_region): if not Region.outlives(value_region, target_region):
if is_aug_assign:
target_desc = "the assignment target, allocated here,"
else:
target_desc = "the assignment target"
note = diagnostic.Diagnostic("note",
"this expression has type {type}",
{"type": types.TypePrinter().name(value.type)},
value.loc)
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"the assigned value does not outlive the assignment target", {}, "the assigned value does not outlive the assignment target", {},
value.loc, [target.loc], value.loc, [target.loc],
notes=self._diagnostics_for(target_region, target.loc, notes=self._diagnostics_for(target_region, target.loc,
target_desc) + "the assignment target") +
self._diagnostics_for(value_region, value.loc, self._diagnostics_for(value_region, value.loc,
"the assigned value")) "the assigned value"))
self.engine.process(diag) self.engine.process(diag)
@ -339,9 +331,19 @@ class EscapeValidator(algorithm.Visitor):
def visit_AugAssign(self, node): def visit_AugAssign(self, node):
if builtins.is_allocated(node.target.type): if builtins.is_allocated(node.target.type):
# If the target is mutable, op-assignment will allocate note = diagnostic.Diagnostic("note",
# in the youngest region. "try using `{lhs} = {lhs} {op} {rhs}` instead",
self.visit_assignment(node.target, node.value, is_aug_assign=True) {"lhs": node.target.loc.source(),
"rhs": node.value.loc.source(),
"op": node.op.loc.source()[:-1]},
node.loc)
diag = diagnostic.Diagnostic("error",
"values cannot be mutated in-place", {},
node.op.loc, [node.target.loc],
notes=[note])
self.engine.process(diag)
self.visit_assignment(node.target, node.value)
def visit_Return(self, node): def visit_Return(self, node):
region = self._region_of(node.value) region = self._region_of(node.value)

View File

@ -545,7 +545,7 @@ class CommKernel:
self._write_string(function) self._write_string(function)
else: else:
exn_type = type(exn) exn_type = type(exn)
if exn_type in (ZeroDivisionError, ValueError, IndexError) or \ if exn_type in (ZeroDivisionError, ValueError, IndexError, RuntimeError) or \
hasattr(exn, "artiq_builtin"): hasattr(exn, "artiq_builtin"):
self._write_string("0:{}".format(exn_type.__name__)) self._write_string("0:{}".format(exn_type.__name__))
else: else:

View File

@ -172,8 +172,8 @@ class DDSChannel:
The time cursor is not modified by any function in this class. The time cursor is not modified by any function in this class.
:param bus: name of the DDS bus device that this DDS is connected to. :param bus_channel: RTIO channel number of the DDS bus.
:param channel: channel number of the DDS device to control. :param channel: channel number (on the bus) of the DDS device to control.
""" """
kernel_invariants = { kernel_invariants = {
@ -291,8 +291,8 @@ class DDSGroupAD9914(DDSGroup):
self.write_duration_mu = 5 * self.rtio_period_mu self.write_duration_mu = 5 * self.rtio_period_mu
self.dac_cal_duration_mu = 147000 * self.rtio_period_mu self.dac_cal_duration_mu = 147000 * self.rtio_period_mu
self.init_duration_mu = 8 * self.write_duration_mu + self.dac_cal_duration_mu self.init_duration_mu = 9 * self.write_duration_mu + self.dac_cal_duration_mu
self.init_sync_duration_mu = 16 * self.write_duration_mu + 2 * self.dac_cal_duration_mu self.init_sync_duration_mu = 17 * self.write_duration_mu + 2 * self.dac_cal_duration_mu
self.program_duration_mu = 6 * self.write_duration_mu self.program_duration_mu = 6 * self.write_duration_mu
self.continuous_phase_comp = [0] * (self.dds_bus_count * self.dds_channel_count) self.continuous_phase_comp = [0] * (self.dds_bus_count * self.dds_channel_count)

View File

@ -10,6 +10,7 @@ from artiq.coredevice.runtime import source_loader
ZeroDivisionError = builtins.ZeroDivisionError ZeroDivisionError = builtins.ZeroDivisionError
ValueError = builtins.ValueError ValueError = builtins.ValueError
IndexError = builtins.IndexError IndexError = builtins.IndexError
RuntimeError = builtins.RuntimeError
class CoreException: class CoreException:
@ -71,8 +72,8 @@ class CacheError(Exception):
class RTIOUnderflow(Exception): class RTIOUnderflow(Exception):
"""Raised when the CPU fails to submit a RTIO event early enough """Raised when the CPU or DMA core fails to submit a RTIO event early
(with respect to the event's timestamp). enough (with respect to the event's timestamp).
The offending event is discarded and the RTIO core keeps operating. The offending event is discarded and the RTIO core keeps operating.
""" """

View File

@ -32,7 +32,7 @@ class Config:
Exposes the configurable quantities of a single SAWG channel. Exposes the configurable quantities of a single SAWG channel.
Access to the configuration registers for a SAWG channel can not Access to the configuration registers for a SAWG channel can not
be concurrent. There must be at least :attr:_rtio_interval` machine be concurrent. There must be at least :attr:`_rtio_interval` machine
units of delay between accesses. Replacement is not supported and will be units of delay between accesses. Replacement is not supported and will be
lead to an ``RTIOCollision`` as this is likely a programming error. lead to an ``RTIOCollision`` as this is likely a programming error.
All methods therefore advance the timeline by the duration of one All methods therefore advance the timeline by the duration of one

View File

@ -178,13 +178,17 @@ class Controllers:
raise ValueError raise ValueError
def __setitem__(self, k, v): def __setitem__(self, k, v):
if (isinstance(v, dict) and v["type"] == "controller" and try:
self.host_filter in get_ip_addresses(v["host"])): if (isinstance(v, dict) and v["type"] == "controller" and
v["command"] = v["command"].format(name=k, self.host_filter in get_ip_addresses(v["host"])):
bind=self.host_filter, v["command"] = v["command"].format(name=k,
port=v["port"]) bind=self.host_filter,
self.queue.put_nowait(("set", (k, v))) port=v["port"])
self.active_or_queued.add(k) self.queue.put_nowait(("set", (k, v)))
self.active_or_queued.add(k)
except:
logger.error("Failed to process device database entry %s", k,
exc_info=True)
def __delitem__(self, k): def __delitem__(self, k):
if k in self.active_or_queued: if k in self.active_or_queued:

View File

@ -207,7 +207,8 @@ class _Tcube:
# derived classes must implement this # derived classes must implement this
raise NotImplementedError raise NotImplementedError
async def send_request(self, msgreq_id, wait_for_msgs, param1=0, param2=0, data=None): async def send_request(self, msgreq_id, wait_for_msgs, param1=0, param2=0,
data=None):
await self.send(Message(msgreq_id, param1, param2, data=data)) await self.send(Message(msgreq_id, param1, param2, data=data))
msg = None msg = None
while msg is None or msg.id not in wait_for_msgs: while msg is None or msg.id not in wait_for_msgs:
@ -218,7 +219,7 @@ class _Tcube:
async def set_channel_enable_state(self, activated): async def set_channel_enable_state(self, activated):
"""Enable or Disable channel 1. """Enable or Disable channel 1.
:param activated: 1 to enable channel, 2 to disable it. :param activated: 1 to enable channel, 0 to disable it.
""" """
if activated: if activated:
@ -283,9 +284,13 @@ class _Tcube:
class Tpz(_Tcube): class Tpz(_Tcube):
"""Either :py:meth:`set_tpz_io_settings()<Tpz.set_tpz_io_settings>`
or :py:meth:`get_tpz_io_settings()<Tpz.get_tpz_io_settings>` must
be completed to finish initialising the driver.
"""
def __init__(self, serial_dev): def __init__(self, serial_dev):
_Tcube.__init__(self, serial_dev) _Tcube.__init__(self, serial_dev)
self.voltage_limit = self.get_tpz_io_settings()[0] self.voltage_limit = None
async def handle_message(self, msg): async def handle_message(self, msg):
msg_id = msg.id msg_id = msg.id
@ -336,7 +341,7 @@ class Tpz(_Tcube):
:rtype: int :rtype: int
""" """
get_msg = await self.send_request(MGMSG.PZ_REQ_POSCONTROLMODE, get_msg = await self.send_request(MGMSG.PZ_REQ_POSCONTROLMODE,
[MGMSG.PZ_GET_POSCONTROLMODE], 1) [MGMSG.PZ_GET_POSCONTROLMODE], 1)
return get_msg.param2 return get_msg.param2
async def set_output_volts(self, voltage): async def set_output_volts(self, voltage):
@ -348,9 +353,8 @@ class Tpz(_Tcube):
:param voltage: The output voltage applied to the piezo when operating :param voltage: The output voltage applied to the piezo when operating
in open loop mode. The voltage value must be in range in open loop mode. The voltage value must be in range
[0; voltage_limit]. Voltage_limit being set by the [0; voltage_limit]. Voltage_limit being set by the
:py:meth:`set_tpz_io_settings() :py:meth:`set_tpz_io_settings()<Tpz.set_tpz_io_settings>`
<artiq.devices.thorlabs_tcube.driver.Tpz.set_tpz_io_settings>` method method between the three values 75 V, 100 V and 150 V.
between the three values 75 V, 100 V and 150 V.
""" """
if voltage < 0 or voltage > self.voltage_limit: if voltage < 0 or voltage > self.voltage_limit:
raise ValueError("Voltage must be in range [0;{}]" raise ValueError("Voltage must be in range [0;{}]"
@ -366,7 +370,7 @@ class Tpz(_Tcube):
:rtype: float :rtype: float
""" """
get_msg = await self.send_request(MGMSG.PZ_REQ_OUTPUTVOLTS, get_msg = await self.send_request(MGMSG.PZ_REQ_OUTPUTVOLTS,
[MGMSG.PZ_GET_OUTPUTVOLTS], 1) [MGMSG.PZ_GET_OUTPUTVOLTS], 1)
return st.unpack("<H", get_msg.data[2:])[0]*self.voltage_limit/32767 return st.unpack("<H", get_msg.data[2:])[0]*self.voltage_limit/32767
async def set_output_position(self, position_sw): async def set_output_position(self, position_sw):
@ -404,7 +408,7 @@ class Tpz(_Tcube):
0x00 Software Only: Unit responds only to software inputs and the 0x00 Software Only: Unit responds only to software inputs and the
HV amp output is that set using the :py:meth:`set_output_volts() HV amp output is that set using the :py:meth:`set_output_volts()
<artiq.devices.thorlabs_tcube.driver.Tpz.set_output_volts>` method. <Tpz.set_output_volts>` method.
0x01 External Signal: Unit sums the differential signal on the rear 0x01 External Signal: Unit sums the differential signal on the rear
panel EXT IN(+) and EXT IN(-) connectors with the voltage set panel EXT IN(+) and EXT IN(-) connectors with the voltage set
@ -426,13 +430,12 @@ class Tpz(_Tcube):
amplifier circuit. amplifier circuit.
:return: Value which selects the various analog sources, cf. :return: Value which selects the various analog sources, cf.
:py:meth:`set_input_volts_source() :py:meth:`set_input_volts_source()<Tpz.set_input_volts_source>`
<artiq.devices.thorlabs_tcube.driver.Tpz.set_input_volts_source>` method method docstring for meaning of bits.
docstring for meaning of bits.
:rtype: int :rtype: int
""" """
get_msg = await self.send_request(MGMSG.PZ_REQ_INPUTVOLTSSRC, get_msg = await self.send_request(MGMSG.PZ_REQ_INPUTVOLTSSRC,
[MGMSG.PZ_GET_INPUTVOLTSSRC], 1) [MGMSG.PZ_GET_INPUTVOLTSSRC], 1)
return st.unpack("<H", get_msg.data[2:])[0] return st.unpack("<H", get_msg.data[2:])[0]
async def set_pi_constants(self, prop_const, int_const): async def set_pi_constants(self, prop_const, int_const):
@ -459,7 +462,7 @@ class Tpz(_Tcube):
:rtype: a 2 int elements tuple : (int, int) :rtype: a 2 int elements tuple : (int, int)
""" """
get_msg = await self.send_request(MGMSG.PZ_REQ_PICONSTS, get_msg = await self.send_request(MGMSG.PZ_REQ_PICONSTS,
[MGMSG.PZ_GET_PICONSTS], 1) [MGMSG.PZ_GET_PICONSTS], 1)
return st.unpack("<HH", get_msg.data[2:]) return st.unpack("<HH", get_msg.data[2:])
async def set_output_lut(self, lut_index, output): async def set_output_lut(self, lut_index, output):
@ -488,7 +491,7 @@ class Tpz(_Tcube):
applicable channel is specified by the Chan Ident parameter If only a applicable channel is specified by the Chan Ident parameter If only a
sub set of the array is being used (as specified by the cyclelength sub set of the array is being used (as specified by the cyclelength
parameter of the :py:meth:`set_output_lut_parameters() parameter of the :py:meth:`set_output_lut_parameters()
<artiq.devices.thorlabs_tcube.driver.Tpz.set_output_lut_parameters>` <Tpz.set_output_lut_parameters>`
function), then only the first cyclelength values need to be set. In function), then only the first cyclelength values need to be set. In
this manner, any arbitrary voltage waveform can be programmed into the this manner, any arbitrary voltage waveform can be programmed into the
LUT. Note. The LUT values are output by the system at a maximum LUT. Note. The LUT values are output by the system at a maximum
@ -499,8 +502,8 @@ class Tpz(_Tcube):
to 512 for TPZ). to 512 for TPZ).
:param output: The voltage value to be set. Values are in the range :param output: The voltage value to be set. Values are in the range
[0; voltage_limit]. Voltage_limit being set with the [0; voltage_limit]. Voltage_limit being set with the
:py:meth:`set_tpz_io_settings :py:meth:`set_tpz_io_settings<Tpz.set_tpz_io_settings>`
<artiq.devices.thorlabs_tcube.driver.Tpz.set_tpz_io_settings>` method. method.
""" """
volt = round(output*32767/self.voltage_limit) volt = round(output*32767/self.voltage_limit)
payload = st.pack("<HHH", 1, lut_index, volt) payload = st.pack("<HHH", 1, lut_index, volt)
@ -519,7 +522,8 @@ class Tpz(_Tcube):
return index, output*self.voltage_limit/32767 return index, output*self.voltage_limit/32767
async def set_output_lut_parameters(self, mode, cycle_length, num_cycles, async def set_output_lut_parameters(self, mode, cycle_length, num_cycles,
delay_time, precycle_rest, postcycle_rest): delay_time, precycle_rest,
postcycle_rest):
"""Set Waveform Generator Mode parameters. """Set Waveform Generator Mode parameters.
It is possible to use the controller in an arbitrary Waveform It is possible to use the controller in an arbitrary Waveform
@ -680,8 +684,8 @@ class Tpz(_Tcube):
:return: Returns a tuple whose elements are the voltage limit and the :return: Returns a tuple whose elements are the voltage limit and the
Hub analog input. Refer to :py:meth:`set_tpz_io_settings() Hub analog input. Refer to :py:meth:`set_tpz_io_settings()
<artiq.devices.thorlabs_tcube.driver.Tpz.set_tpz_io_settings>` for the <Tpz.set_tpz_io_settings>` for
meaning of those parameters. the meaning of those parameters.
:rtype: a 2 elements tuple (int, int) :rtype: a 2 elements tuple (int, int)
""" """
get_msg = await self.send_request(MGMSG.PZ_REQ_TPZ_IOSETTINGS, get_msg = await self.send_request(MGMSG.PZ_REQ_TPZ_IOSETTINGS,
@ -760,8 +764,7 @@ class Tdc(_Tcube):
:return: An 8 int tuple containing the following values: zero_wnd, :return: An 8 int tuple containing the following values: zero_wnd,
vel1, wnd1, vel2, wnd2, vel3, wnd3, vel4. See vel1, wnd1, vel2, wnd2, vel3, wnd3, vel4. See
:py:meth:`set_pot_parameters() :py:meth:`set_pot_parameters()<Tdc.set_pot_parameters>` for a
<artiq.devices.thorlabs_tcube.driver.Tdc.set_pot_parameters>` for a
description of each tuple element meaning. description of each tuple element meaning.
:rtype: An 8 int tuple :rtype: An 8 int tuple
""" """
@ -839,7 +842,7 @@ class Tdc(_Tcube):
return st.unpack("<LL", get_msg.data[6:]) return st.unpack("<LL", get_msg.data[6:])
async def set_jog_parameters(self, mode, step_size, acceleration, async def set_jog_parameters(self, mode, step_size, acceleration,
max_velocity, stop_mode): max_velocity, stop_mode):
"""Set the velocity jog parameters. """Set the velocity jog parameters.
:param mode: 1 for continuous jogging, 2 for single step jogging. :param mode: 1 for continuous jogging, 2 for single step jogging.
@ -883,7 +886,7 @@ class Tdc(_Tcube):
:rtype: int :rtype: int
""" """
get_msg = await self.send_request(MGMSG.MOT_REQ_GENMOVEPARAMS, get_msg = await self.send_request(MGMSG.MOT_REQ_GENMOVEPARAMS,
[MGMSG.MOT_GET_GENMOVEPARAMS], 1) [MGMSG.MOT_GET_GENMOVEPARAMS], 1)
return st.unpack("<l", get_msg.data[2:])[0] return st.unpack("<l", get_msg.data[2:])[0]
async def set_move_relative_parameters(self, relative_distance): async def set_move_relative_parameters(self, relative_distance):
@ -950,7 +953,8 @@ class Tdc(_Tcube):
This call is blocking until device is homed or move is stopped. This call is blocking until device is homed or move is stopped.
""" """
await self.send_request(MGMSG.MOT_MOVE_HOME, await self.send_request(MGMSG.MOT_MOVE_HOME,
[MGMSG.MOT_MOVE_HOMED, MGMSG.MOT_MOVE_STOPPED], 1) [MGMSG.MOT_MOVE_HOMED, MGMSG.MOT_MOVE_STOPPED],
1)
async def set_limit_switch_parameters(self, cw_hw_limit, ccw_hw_limit): async def set_limit_switch_parameters(self, cw_hw_limit, ccw_hw_limit):
"""Set the limit switch parameters. """Set the limit switch parameters.
@ -988,7 +992,7 @@ class Tdc(_Tcube):
:return: A 2 int tuple containing the following in order: cw_hw_limit, :return: A 2 int tuple containing the following in order: cw_hw_limit,
ccw_hw_limit. Cf. description in ccw_hw_limit. Cf. description in
:py:meth:`set_limit_switch_parameters() :py:meth:`set_limit_switch_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_limit_switch_parameters>` <Tdc.set_limit_switch_parameters>`
method. method.
:rtype: A 2 int tuple. :rtype: A 2 int tuple.
""" """
@ -1001,11 +1005,13 @@ class Tdc(_Tcube):
The relative distance parameter used for the move will be the parameter The relative distance parameter used for the move will be the parameter
sent previously by a :py:meth:`set_move_relative_parameters() sent previously by a :py:meth:`set_move_relative_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_move_relative_parameters>` <Tdc.set_move_relative_parameters>`
command. command.
""" """
await self.send_request(MGMSG.MOT_MOVE_RELATIVE, await self.send_request(MGMSG.MOT_MOVE_RELATIVE,
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED], 1) [MGMSG.MOT_MOVE_COMPLETED,
MGMSG.MOT_MOVE_STOPPED],
1)
async def move_relative(self, relative_distance): async def move_relative(self, relative_distance):
"""Start a relative move """Start a relative move
@ -1015,7 +1021,8 @@ class Tdc(_Tcube):
""" """
payload = st.pack("<Hl", 1, relative_distance) payload = st.pack("<Hl", 1, relative_distance)
await self.send_request(MGMSG.MOT_MOVE_RELATIVE, await self.send_request(MGMSG.MOT_MOVE_RELATIVE,
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED], [MGMSG.MOT_MOVE_COMPLETED,
MGMSG.MOT_MOVE_STOPPED],
data=payload) data=payload)
async def move_absolute_memory(self): async def move_absolute_memory(self):
@ -1023,11 +1030,12 @@ class Tdc(_Tcube):
The absolute move position parameter used for the move will be the The absolute move position parameter used for the move will be the
parameter sent previously by a :py:meth:`set_move_absolute_parameters() parameter sent previously by a :py:meth:`set_move_absolute_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_move_absolute_parameters>` <Tdc.set_move_absolute_parameters>`
command. command.
""" """
await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE, await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE,
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED], [MGMSG.MOT_MOVE_COMPLETED,
MGMSG.MOT_MOVE_STOPPED],
param1=1) param1=1)
async def move_absolute(self, absolute_distance): async def move_absolute(self, absolute_distance):
@ -1039,7 +1047,8 @@ class Tdc(_Tcube):
""" """
payload = st.pack("<Hl", 1, absolute_distance) payload = st.pack("<Hl", 1, absolute_distance)
await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE, await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE,
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED], [MGMSG.MOT_MOVE_COMPLETED,
MGMSG.MOT_MOVE_STOPPED],
data=payload) data=payload)
async def move_jog(self, direction): async def move_jog(self, direction):
@ -1048,7 +1057,8 @@ class Tdc(_Tcube):
:param direction: The direction to jog. 1 is forward, 2 is backward. :param direction: The direction to jog. 1 is forward, 2 is backward.
""" """
await self.send_request(MGMSG.MOT_MOVE_JOG, await self.send_request(MGMSG.MOT_MOVE_JOG,
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED], [MGMSG.MOT_MOVE_COMPLETED,
MGMSG.MOT_MOVE_STOPPED],
param1=1, param2=direction) param1=1, param2=direction)
async def move_velocity(self, direction): async def move_velocity(self, direction):
@ -1057,15 +1067,15 @@ class Tdc(_Tcube):
When this method is called, the motor will move continuously in the When this method is called, the motor will move continuously in the
specified direction using the velocity parameter set by the specified direction using the velocity parameter set by the
:py:meth:`set_move_relative_parameters() :py:meth:`set_move_relative_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_move_relative_parameters>` <Tdc.set_move_relative_parameters>`
command until a :py:meth:`move_stop() command until a :py:meth:`move_stop()<Tdc.move_stop>` command (either
<artiq.devices.thorlabs_tcube.driver.Tdc.move_stop>` command (either
StopImmediate or StopProfiled) is called, or a limit switch is reached. StopImmediate or StopProfiled) is called, or a limit switch is reached.
:param direction: The direction to jog: 1 to move forward, 2 to move :param direction: The direction to jog: 1 to move forward, 2 to move
backward. backward.
""" """
await self.send(Message(MGMSG.MOT_MOVE_VELOCITY, param1=1, param2=direction)) await self.send(Message(MGMSG.MOT_MOVE_VELOCITY, param1=1,
param2=direction))
async def move_stop(self, stop_mode): async def move_stop(self, stop_mode):
"""Stop any type of motor move. """Stop any type of motor move.
@ -1080,11 +1090,11 @@ class Tdc(_Tcube):
if await self.is_moving(): if await self.is_moving():
await self.send_request(MGMSG.MOT_MOVE_STOP, await self.send_request(MGMSG.MOT_MOVE_STOP,
[MGMSG.MOT_MOVE_STOPPED, [MGMSG.MOT_MOVE_STOPPED,
MGMSG.MOT_MOVE_COMPLETED], MGMSG.MOT_MOVE_COMPLETED],
1, stop_mode) 1, stop_mode)
async def set_dc_pid_parameters(self, proportional, integral, differential, async def set_dc_pid_parameters(self, proportional, integral, differential,
integral_limit, filter_control=0x0F): integral_limit, filter_control=0x0F):
"""Set the position control loop parameters. """Set the position control loop parameters.
:param proportional: The proportional gain, values in range [0; 32767]. :param proportional: The proportional gain, values in range [0; 32767].
@ -1108,8 +1118,8 @@ class Tdc(_Tcube):
:return: A 5 int tuple containing in this order: :return: A 5 int tuple containing in this order:
proportional gain, integral gain, differential gain, integral limit proportional gain, integral gain, differential gain, integral limit
and filter control. Cf. :py:meth:`set_dc_pid_parameters() and filter control. Cf. :py:meth:`set_dc_pid_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_dc_pid_parameters>` for <Tdc.set_dc_pid_parameters>`
precise description. for precise description.
:rtype: A 5 int tuple. :rtype: A 5 int tuple.
""" """
get_msg = await self.send_request(MGMSG.MOT_REQ_DCPIDPARAMS, get_msg = await self.send_request(MGMSG.MOT_REQ_DCPIDPARAMS,
@ -1151,10 +1161,10 @@ class Tdc(_Tcube):
:param mode: If set to 1, the buttons are used to jog the motor. Once :param mode: If set to 1, the buttons are used to jog the motor. Once
set to this mode, the move parameters for the buttons are taken set to this mode, the move parameters for the buttons are taken
from the arguments of the :py:meth:`set_jog_parameters() from the arguments of the :py:meth:`set_jog_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_jog_parameters>` method. <Tdc.set_jog_parameters>`
If set to 2, each button can be programmed with a differente method. If set to 2, each button can be programmed with a
position value such that the controller will move the motor to that differente position value such that the controller will move the
position when the specific button is pressed. motor to that position when the specific button is pressed.
:param position1: The position (in encoder counts) to which the motor :param position1: The position (in encoder counts) to which the motor
will move when the top button is pressed. will move when the top button is pressed.
:param position2: The position (in encoder counts) to which the motor :param position2: The position (in encoder counts) to which the motor
@ -1169,8 +1179,8 @@ class Tdc(_Tcube):
:return: A 3 int tuple containing in this order: button mode, :return: A 3 int tuple containing in this order: button mode,
position1 and position2. Cf. :py:meth:`set_button_parameters() position1 and position2. Cf. :py:meth:`set_button_parameters()
<artiq.devices.thorlabs_tcube.driver.Tdc.set_button_parameters>` for <Tdc.set_button_parameters>`
description. for description.
:rtype: A 3 int tuple :rtype: A 3 int tuple
""" """
get_msg = await self.send_request(MGMSG.MOT_REQ_BUTTONPARAMS, get_msg = await self.send_request(MGMSG.MOT_REQ_BUTTONPARAMS,

View File

@ -2,7 +2,7 @@
# The RTIO channel numbers here are for NIST CLOCK on KC705. # The RTIO channel numbers here are for NIST CLOCK on KC705.
# The list of devices here is not exhaustive. # The list of devices here is not exhaustive.
core_addr = "kc705.lab.m-labs.hk" core_addr = "kc705-1.lab.m-labs.hk"
device_db = { device_db = {
"core": { "core": {
@ -33,7 +33,7 @@ device_db = {
"class": "DDSGroupAD9914", "class": "DDSGroupAD9914",
"arguments": { "arguments": {
"sysclk": 3e9, "sysclk": 3e9,
"first_dds_bus_channel": 26, "first_dds_bus_channel": 27,
"dds_bus_count": 2, "dds_bus_count": 2,
"dds_channel_count": 3 "dds_channel_count": 3
} }

View File

@ -44,7 +44,7 @@ class TDR(EnvExperiment):
try: try:
self.many(n, self.core.seconds_to_mu(pulse)) self.many(n, self.core.seconds_to_mu(pulse))
except PulseNotReceivedError: except PulseNotReceivedError:
print("to few edges: cable too long or wiring bad") print("too few edges: cable too long or wiring bad")
else: else:
print(self.t) print(self.t)
t_rise = mu_to_seconds(self.t[0], self.core)/n - latency t_rise = mu_to_seconds(self.t[0], self.core)/n - latency

View File

@ -1,7 +1,3 @@
[root]
name = "std_artiq"
version = "0.0.0"
[[package]] [[package]]
name = "alloc_list" name = "alloc_list"
version = "0.0.0" version = "0.0.0"
@ -17,23 +13,38 @@ dependencies = [
"board 0.0.0", "board 0.0.0",
] ]
[[package]]
name = "backtrace_artiq"
version = "0.0.0"
[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "board" name = "board"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "build_artiq" name = "build_artiq"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.0.0" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -41,7 +52,7 @@ name = "compiler_builtins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568#631b5687b24af413fdbffa4c2644484e60660b00" source = "git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568#631b5687b24af413fdbffa4c2644484e60660b00"
dependencies = [ dependencies = [
"gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -55,8 +66,8 @@ name = "drtioaux"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"board 0.0.0", "board 0.0.0",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"std_artiq 0.0.0", "std_artiq 0.0.0",
] ]
@ -64,17 +75,24 @@ dependencies = [
name = "dyld" name = "dyld"
version = "0.0.0" version = "0.0.0"
[[package]]
name = "eh"
version = "0.0.0"
dependencies = [
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "fringe" name = "fringe"
version = "1.1.0" version = "1.1.0"
source = "git+https://github.com/m-labs/libfringe?rev=bd23494#bd2349467157969324ca7da5d2ae033c7ffac0c0" source = "git+https://github.com/m-labs/libfringe?rev=b8a6d8f#b8a6d8f68df0edaa3d67d9f3b7b62af9d3bb64a5"
dependencies = [ dependencies = [
"libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gcc" name = "gcc"
version = "0.3.42" version = "0.3.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -94,26 +112,35 @@ dependencies = [
"amp 0.0.0", "amp 0.0.0",
"board 0.0.0", "board 0.0.0",
"build_artiq 0.0.0", "build_artiq 0.0.0",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dyld 0.0.0", "dyld 0.0.0",
"eh 0.0.0",
"proto 0.0.0", "proto 0.0.0",
"std_artiq 0.0.0", "std_artiq 0.0.0",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.18" version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "log" name = "log"
version = "0.3.6" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "log_buffer" name = "log_buffer"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -121,23 +148,23 @@ name = "logger_artiq"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"board 0.0.0", "board 0.0.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "managed" name = "managed"
version = "0.4.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "proto" name = "proto"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dyld 0.0.0", "dyld 0.0.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"std_artiq 0.0.0", "std_artiq 0.0.0",
] ]
@ -147,17 +174,18 @@ version = "0.0.0"
dependencies = [ dependencies = [
"alloc_list 0.0.0", "alloc_list 0.0.0",
"amp 0.0.0", "amp 0.0.0",
"backtrace_artiq 0.0.0",
"board 0.0.0", "board 0.0.0",
"build_artiq 0.0.0", "build_artiq 0.0.0",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"drtioaux 0.0.0", "drtioaux 0.0.0",
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)", "fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"logger_artiq 0.0.0", "logger_artiq 0.0.0",
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proto 0.0.0", "proto 0.0.0",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=6f5ae33)", "smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=92e970b)",
"std_artiq 0.0.0", "std_artiq 0.0.0",
] ]
@ -166,6 +194,15 @@ name = "rustc-cfg"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "same-file"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "satman" name = "satman"
version = "0.0.0" version = "0.0.0"
@ -175,7 +212,7 @@ dependencies = [
"build_artiq 0.0.0", "build_artiq 0.0.0",
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)", "compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)",
"drtioaux 0.0.0", "drtioaux 0.0.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"logger_artiq 0.0.0", "logger_artiq 0.0.0",
"std_artiq 0.0.0", "std_artiq 0.0.0",
] ]
@ -183,19 +220,25 @@ dependencies = [
[[package]] [[package]]
name = "smoltcp" name = "smoltcp"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/m-labs/smoltcp?rev=6f5ae33#6f5ae33501827d57926469c6f1a860205a24f7ae" source = "git+https://github.com/m-labs/smoltcp?rev=92e970b#92e970b3798737cfaa2a829d3f1bb7a7353696ee"
dependencies = [ dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "std_artiq"
version = "0.0.0"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "1.0.3" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -210,18 +253,22 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
"checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)" = "<none>" "checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)" = "<none>"
"checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a" "checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)" = "<none>" "checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)" = "<none>"
"checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" "checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec57723b84bbe7bdf76aa93169c9b59e67473317c6de3a83cb2a0f8ccb2aa493" "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
"checksum managed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d48e8c30a4363e2981fe4db20527f6ab0f32a243bbc75379dea5a64f60dae4" "checksum log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f033173c9486b7fe97a79c895c0a3483ae395ab6744c985d10078950e2492419"
"checksum managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6713e624266d7600e9feae51b1926c6a6a6bebb18ec5a8e11a5f1d5661baba"
"checksum rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a596b5718bf5e059d59a30af12f7f462a152de147aa462b70892849ee18704" "checksum rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a596b5718bf5e059d59a30af12f7f462a152de147aa462b70892849ee18704"
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=6f5ae33)" = "<none>" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7c16466ecc507c7cb5988db03e6eab4aaeab89a5c37a29251fcfd3ac9b7afe" "checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=92e970b)" = "<none>"
"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@ -1,2 +1,7 @@
[workspace] [workspace]
members = ["runtime", "ksupport", "satman"] members = ["runtime", "ksupport", "satman"]
[profile.dev]
incremental = false # incompatible with LTO
lto = true
debug = 2

View File

@ -17,6 +17,7 @@ byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" } cslice = { version = "0.3" }
alloc_stub = { path = "../liballoc_stub" } alloc_stub = { path = "../liballoc_stub" }
std_artiq = { path = "../libstd_artiq" } std_artiq = { path = "../libstd_artiq" }
eh = { path = "../libeh" }
dyld = { path = "../libdyld" } dyld = { path = "../libdyld" }
board = { path = "../libboard" } board = { path = "../libboard" }
proto = { path = "../libproto" } proto = { path = "../libproto" }

View File

@ -5,32 +5,25 @@ CFLAGS += \
-I$(LIBUNWIND_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY) \
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include \ -I$(LIBUNWIND_DIRECTORY)/../unwinder/include \
-I$(MISOC_DIRECTORY)/software/include/dyld -I$(MISOC_DIRECTORY)/software/include/dyld
CFLAGS += -DNDEBUG
LDFLAGS += --eh-frame-hdr \ LDFLAGS += --eh-frame-hdr \
-L../libcompiler-rt \ -L../libcompiler-rt \
-L../libbase \ -L../libbase \
-L../libprintf \
-L../libm \ -L../libm \
-L../libunwind -L../libunwind
RUSTFLAGS += -Cpanic=unwind RUSTFLAGS += -Cpanic=unwind
all: ksupport.elf all:: ksupport.elf
.PHONY: $(RUSTOUT)/libksupport.a .PHONY: $(RUSTOUT)/libksupport.a
$(RUSTOUT)/libksupport.a: $(RUSTOUT)/libksupport.a:
$(cargo) --manifest-path $(KSUPPORT_DIRECTORY)/Cargo.toml $(cargo) --manifest-path $(KSUPPORT_DIRECTORY)/Cargo.toml
ksupport.elf: $(RUSTOUT)/libksupport.a glue.o ksupport.elf: $(RUSTOUT)/libksupport.a glue.o
$(LD) $(LDFLAGS) -T $(KSUPPORT_DIRECTORY)/ksupport.ld -o $@ $^ \ $(link) -T $(KSUPPORT_DIRECTORY)/ksupport.ld \
-lunwind -lcompiler-rt -lbase -lm -lunwind-elf -lcompiler-rt -lprintf-float -lm
@chmod -x $@
%.o: $(KSUPPORT_DIRECTORY)/%.c %.o: $(KSUPPORT_DIRECTORY)/%.c
$(compile) $(compile)
clean:
$(RM) *.o ksupport.elf
$(RM) -rf cargo
.PHONY: all clean

View File

@ -75,9 +75,9 @@ static mut API: &'static [(&'static str, *const ())] = &[
/* exceptions */ /* exceptions */
api!(_Unwind_Resume = ::unwind::_Unwind_Resume), api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
api!(__artiq_personality = ::eh::personality), api!(__artiq_personality = ::eh_artiq::personality),
api!(__artiq_raise = ::eh::raise), api!(__artiq_raise = ::eh_artiq::raise),
api!(__artiq_reraise = ::eh::reraise), api!(__artiq_reraise = ::eh_artiq::reraise),
/* proxified syscalls */ /* proxified syscalls */
api!(core_log), api!(core_log),

View File

@ -1,460 +0,0 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_upper_case_globals, non_camel_case_types, dead_code)]
use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;
const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;
const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;
const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}
#[repr(C,packed)]
#[derive(Clone, Copy)]
struct Unaligned<T>(T);
// This contortion is required due to https://github.com/rust-lang/rust/issues/27060.
impl<T> Unaligned<T> {
fn get(self) -> T { self.0 }
}
impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: ptr }
}
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are
// telling the backend to generate "misalignment-safe" code.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = *(self.ptr as *const Unaligned<T>);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result.get()
}
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
unsafe fn read_uleb128(&mut self) -> u64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
unsafe fn read_sleb128(&mut self) -> i64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}
debug_assert!(encoding != DW_EH_PE_omit);
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
_ => panic!(),
};
if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}
result
}
}
fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}
match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}
pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}
unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
exn_name: CSlice<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};
let ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
let class_info;
if ttype_encoding != DW_EH_PE_omit {
let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();
if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}
if cs_lpad == 0 {
return EHAction::None
}
let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}
let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);
if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);
if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}
if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}
if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}
return EHAction::None
}
// the function has a personality but no landing pads; this is fine
EHAction::None
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Exception<'a> {
pub name: CSlice<'a, u8>,
pub file: CSlice<'a, u8>,
pub line: u32,
pub column: u32,
pub function: CSlice<'a, u8>,
pub message: CSlice<'a, u8>,
pub param: [i64; 3]
}
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
const MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let ip = uw::_Unwind_GetIP(context) - 1;
let func_start = uw::_Unwind_GetRegionStart(context);
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
let eh_action = find_eh_action(lsda, func_start, ip, exception.name);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
exception_info.handled = true
}
// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
uw_exception as uw::_Unwind_Word);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
exception_info.exception = None;
}
}
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: false,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};
#[export_name="__artiq_raise"]
#[unwind]
pub unsafe extern fn raise(exception: *const Exception) -> ! {
// Zing! The Exception<'a> as Exception<'static> cast is not really sound in case
// the exception is ever captured. Fortunately, they currently aren't, and we save
// on the hassle of having to allocate exceptions somewhere except on stack.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
uncaught_exception, ptr::null_mut());
unreachable!()
}
#[export_name="__artiq_reraise"]
#[unwind]
pub unsafe extern fn reraise() -> ! {
if INFLIGHT.handled {
raise(&INFLIGHT.exception.unwrap())
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
}
}
// Stub implementations for the functions the panic_unwind crate expects to be provided.
// These all do nothing in libunwind, but aren't built for OR1K.
pub mod stubs {
#![allow(bad_style, unused_variables)]
use super::{uw, c_int};
#[export_name="_Unwind_GetIPInfo"]
pub unsafe extern fn _Unwind_GetIPInfo(ctx: *mut uw::_Unwind_Context,
ip_before_insn: *mut c_int) -> uw::_Unwind_Word {
*ip_before_insn = 0;
uw::_Unwind_GetIP(ctx)
}
#[export_name="_Unwind_GetTextRelBase"]
pub unsafe extern fn _Unwind_GetTextRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
#[export_name="_Unwind_GetDataRelBase"]
pub unsafe extern fn _Unwind_GetDataRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
}

View File

@ -0,0 +1,203 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(private_no_mangle_fns, non_camel_case_types)]
use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};
use eh::dwarf::{self, EHAction};
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Exception<'a> {
pub name: CSlice<'a, u8>,
pub file: CSlice<'a, u8>,
pub line: u32,
pub column: u32,
pub function: CSlice<'a, u8>,
pub message: CSlice<'a, u8>,
pub param: [i64; 3]
}
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
const MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let ip = uw::_Unwind_GetIP(context) - 1;
let func_start = uw::_Unwind_GetRegionStart(context);
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
let eh_action = dwarf::find_eh_action(lsda, func_start, ip, exception.name);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
exception_info.handled = true
}
// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
uw_exception as uw::_Unwind_Word);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
exception_info.exception = None;
}
}
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: true,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};
#[export_name="__artiq_raise"]
#[unwind(allowed)]
pub unsafe extern fn raise(exception: *const Exception) -> ! {
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
// the exception is ever captured. Fortunately, they currently aren't, and we save
// on the hassle of having to allocate exceptions somewhere except on stack.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
uncaught_exception, ptr::null_mut());
unreachable!()
}
#[export_name="__artiq_reraise"]
#[unwind(allowed)]
pub unsafe extern fn reraise() -> ! {
use cslice::AsCSlice;
if INFLIGHT.handled {
match INFLIGHT.exception {
Some(ref exception) => raise(exception),
None => raise(&Exception {
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
file: file!().as_c_slice(),
line: line!(),
column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719
function: "__artiq_reraise".as_c_slice(),
message: "No active exception to reraise".as_c_slice(),
param: [0, 0, 0]
})
}
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
}
}

View File

@ -19,6 +19,8 @@ void send_to_rtio_log(long long int timestamp, struct slice data);
#define KERNELCPU_LAST_ADDRESS 0x4fffffff #define KERNELCPU_LAST_ADDRESS 0x4fffffff
#define KSUPPORT_HEADER_SIZE 0x80 #define KSUPPORT_HEADER_SIZE 0x80
FILE *stderr;
/* called by libunwind */ /* called by libunwind */
int fprintf(FILE *stream, const char *fmt, ...) int fprintf(FILE *stream, const char *fmt, ...)
{ {

View File

@ -1,4 +1,5 @@
#![feature(lang_items, asm, libc, panic_unwind, unwind_attributes, global_allocator)] #![feature(lang_items, asm, panic_unwind, libc, unwind_attributes,
panic_implementation, panic_info_message)]
#![no_std] #![no_std]
extern crate unwind; extern crate unwind;
@ -10,6 +11,7 @@ extern crate alloc_stub;
extern crate std_artiq as std; extern crate std_artiq as std;
extern crate board; extern crate board;
extern crate eh;
extern crate dyld; extern crate dyld;
extern crate proto; extern crate proto;
extern crate amp; extern crate amp;
@ -52,10 +54,26 @@ macro_rules! recv {
} }
} }
#[no_mangle] #[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[lang = "panic_fmt"] #[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! { pub fn oom(_layout: core::alloc::Layout) -> ! {
send(&Log(format_args!("panic at {}:{}: {}\n", file, line, args))); unreachable!()
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
send(&Log(format_args!("panic at {}:{}:{}",
location.file(), location.line(), location.column())));
} else {
send(&Log(format_args!("panic at unknown location")));
}
if let Some(message) = info.message() {
send(&Log(format_args!("{}\n", message)));
} else {
send(&Log(format_args!("\n")));
}
send(&RunAborted); send(&RunAborted);
loop {} loop {}
} }
@ -72,25 +90,25 @@ macro_rules! println {
macro_rules! raise { macro_rules! raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({ ($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice; use cslice::AsCSlice;
let exn = $crate::eh::Exception { let exn = $crate::eh_artiq::Exception {
name: concat!("0:artiq.coredevice.exceptions.", $name).as_bytes().as_c_slice(), name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
file: file!().as_bytes().as_c_slice(), file: file!().as_c_slice(),
line: line!(), line: line!(),
column: column!(), column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719 // https://github.com/rust-lang/rfcs/pull/1719
function: "(Rust function)".as_bytes().as_c_slice(), function: "(Rust function)".as_c_slice(),
message: $message.as_bytes().as_c_slice(), message: $message.as_c_slice(),
param: [$param0, $param1, $param2] param: [$param0, $param1, $param2]
}; };
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
unsafe { $crate::eh::raise(&exn) } unsafe { $crate::eh_artiq::raise(&exn) }
}); });
($name:expr, $message:expr) => ({ ($name:expr, $message:expr) => ({
raise!($name, $message, 0, 0, 0) raise!($name, $message, 0, 0, 0)
}); });
} }
pub mod eh; mod eh_artiq;
mod api; mod api;
mod rtio; mod rtio;
mod nrt_bus; mod nrt_bus;
@ -114,6 +132,7 @@ pub extern fn send_to_rtio_log(timestamp: i64, text: CSlice<u8>) {
rtio::log(timestamp, text.as_ref()) rtio::log(timestamp, text.as_ref())
} }
#[unwind(aborts)]
extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) { extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
while !rpc_queue::empty() {} while !rpc_queue::empty() {}
send(&RpcSend { send(&RpcSend {
@ -124,6 +143,7 @@ extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
}) })
} }
#[unwind(aborts)]
extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ()) { extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ()) {
while rpc_queue::full() {} while rpc_queue::full() {}
rpc_queue::enqueue(|mut slice| { rpc_queue::enqueue(|mut slice| {
@ -146,6 +166,7 @@ extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ())
}) })
} }
#[unwind(allowed)]
extern fn rpc_recv(slot: *mut ()) -> usize { extern fn rpc_recv(slot: *mut ()) -> usize {
send(&RpcRecvRequest(slot)); send(&RpcRecvRequest(slot));
recv!(&RpcRecvReply(ref result) => { recv!(&RpcRecvReply(ref result) => {
@ -153,7 +174,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
&Ok(alloc_size) => alloc_size, &Ok(alloc_size) => alloc_size,
&Err(ref exception) => &Err(ref exception) =>
unsafe { unsafe {
eh::raise(&eh::Exception { eh_artiq::raise(&eh_artiq::Exception {
name: exception.name.as_bytes().as_c_slice(), name: exception.name.as_bytes().as_c_slice(),
file: exception.file.as_bytes().as_c_slice(), file: exception.file.as_bytes().as_c_slice(),
line: exception.line, line: exception.line,
@ -167,7 +188,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
}) })
} }
fn terminate(exception: &eh::Exception, mut backtrace: &mut [usize]) -> ! { fn terminate(exception: &eh_artiq::Exception, backtrace: &mut [usize]) -> ! {
let mut cursor = 0; let mut cursor = 0;
for index in 0..backtrace.len() { for index in 0..backtrace.len() {
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS { if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
@ -193,6 +214,7 @@ fn terminate(exception: &eh::Exception, mut backtrace: &mut [usize]) -> ! {
loop {} loop {}
} }
#[unwind(allowed)]
extern fn watchdog_set(ms: i64) -> i32 { extern fn watchdog_set(ms: i64) -> i32 {
if ms < 0 { if ms < 0 {
raise!("ValueError", "cannot set a watchdog with a negative timeout") raise!("ValueError", "cannot set a watchdog with a negative timeout")
@ -202,10 +224,12 @@ extern fn watchdog_set(ms: i64) -> i32 {
recv!(&WatchdogSetReply { id } => id) as i32 recv!(&WatchdogSetReply { id } => id) as i32
} }
#[unwind(aborts)]
extern fn watchdog_clear(id: i32) { extern fn watchdog_clear(id: i32) {
send(&WatchdogClear { id: id as usize }) send(&WatchdogClear { id: id as usize })
} }
#[unwind(aborts)]
extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> { extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
send(&CacheGetRequest { send(&CacheGetRequest {
key: str::from_utf8(key.as_ref()).unwrap() key: str::from_utf8(key.as_ref()).unwrap()
@ -213,6 +237,7 @@ extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
recv!(&CacheGetReply { value } => value.as_c_slice()) recv!(&CacheGetReply { value } => value.as_c_slice())
} }
#[unwind(allowed)]
extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) { extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
send(&CachePutRequest { send(&CachePutRequest {
key: str::from_utf8(key.as_ref()).unwrap(), key: str::from_utf8(key.as_ref()).unwrap(),
@ -229,15 +254,12 @@ const DMA_BUFFER_SIZE: usize = 64 * 1024;
struct DmaRecorder { struct DmaRecorder {
active: bool, active: bool,
#[allow(dead_code)]
padding: [u8; 3], //https://github.com/rust-lang/rust/issues/41315
data_len: usize, data_len: usize,
buffer: [u8; DMA_BUFFER_SIZE], buffer: [u8; DMA_BUFFER_SIZE],
} }
static mut DMA_RECORDER: DmaRecorder = DmaRecorder { static mut DMA_RECORDER: DmaRecorder = DmaRecorder {
active: false, active: false,
padding: [0; 3],
data_len: 0, data_len: 0,
buffer: [0; DMA_BUFFER_SIZE], buffer: [0; DMA_BUFFER_SIZE],
}; };
@ -249,6 +271,7 @@ fn dma_record_flush() {
} }
} }
#[unwind(allowed)]
extern fn dma_record_start(name: CSlice<u8>) { extern fn dma_record_start(name: CSlice<u8>) {
let name = str::from_utf8(name.as_ref()).unwrap(); let name = str::from_utf8(name.as_ref()).unwrap();
@ -268,6 +291,7 @@ extern fn dma_record_start(name: CSlice<u8>) {
} }
} }
#[unwind(allowed)]
extern fn dma_record_stop(duration: i64) { extern fn dma_record_stop(duration: i64) {
unsafe { unsafe {
dma_record_flush(); dma_record_flush();
@ -289,10 +313,12 @@ extern fn dma_record_stop(duration: i64) {
} }
} }
#[unwind(aborts)]
extern fn dma_record_output(timestamp: i64, channel: i32, address: i32, word: i32) { extern fn dma_record_output(timestamp: i64, channel: i32, address: i32, word: i32) {
dma_record_output_wide(timestamp, channel, address, [word].as_c_slice()) dma_record_output_wide(timestamp, channel, address, [word].as_c_slice())
} }
#[unwind(aborts)]
extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, words: CSlice<i32>) { extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, words: CSlice<i32>) {
assert!(words.len() <= 16); // enforce the hardware limit assert!(words.len() <= 16); // enforce the hardware limit
@ -333,14 +359,15 @@ extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, wor
if DMA_RECORDER.buffer.len() - DMA_RECORDER.data_len < length { if DMA_RECORDER.buffer.len() - DMA_RECORDER.data_len < length {
dma_record_flush() dma_record_flush()
} }
let mut dst = &mut DMA_RECORDER.buffer[DMA_RECORDER.data_len.. let dst = &mut DMA_RECORDER.buffer[DMA_RECORDER.data_len..
DMA_RECORDER.data_len + length]; DMA_RECORDER.data_len + length];
dst[..header_length].copy_from_slice(&header[..]); dst[..header_length].copy_from_slice(&header[..]);
dst[header_length..].copy_from_slice(&data[..]); dst[header_length..].copy_from_slice(&data[..]);
DMA_RECORDER.data_len += length; DMA_RECORDER.data_len += length;
} }
} }
#[unwind(aborts)]
extern fn dma_erase(name: CSlice<u8>) { extern fn dma_erase(name: CSlice<u8>) {
let name = str::from_utf8(name.as_ref()).unwrap(); let name = str::from_utf8(name.as_ref()).unwrap();
@ -353,6 +380,7 @@ struct DmaTrace {
address: i32, address: i32,
} }
#[unwind(allowed)]
extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace { extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
let name = str::from_utf8(name.as_ref()).unwrap(); let name = str::from_utf8(name.as_ref()).unwrap();
@ -372,6 +400,7 @@ extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
}) })
} }
#[unwind(allowed)]
extern fn dma_playback(timestamp: i64, ptr: i32) { extern fn dma_playback(timestamp: i64, ptr: i32) {
assert!(ptr % 64 == 0); assert!(ptr % 64 == 0);
@ -480,18 +509,32 @@ pub unsafe fn main() {
attribute_writeback(typeinfo as *const ()); attribute_writeback(typeinfo as *const ());
} }
// Make sure all async RPCs are processed before exiting.
// Otherwise, if the comms and kernel CPU run in the following sequence:
//
// comms kernel
// ----------------------- -----------------------
// check for async RPC
// post async RPC
// post RunFinished
// check for mailbox
//
// the async RPC would be missed.
send(&RpcFlush);
send(&RunFinished); send(&RunFinished);
loop {} loop {}
} }
#[no_mangle] #[no_mangle]
#[unwind(allowed)]
pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) { pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea) panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
} }
// We don't export this because libbase does. #[no_mangle]
// #[no_mangle] #[unwind(allowed)]
pub extern fn abort() { pub extern fn abort() {
panic!("aborted") panic!("aborted")
} }

View File

@ -96,6 +96,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> u64 {
} }
if status & RTIO_I_STATUS_OVERFLOW != 0 { if status & RTIO_I_STATUS_OVERFLOW != 0 {
csr::rtio::i_overflow_reset_write(1);
raise!("RTIOOverflow", raise!("RTIOOverflow",
"RTIO input overflow on channel {0}", "RTIO input overflow on channel {0}",
channel as i64, 0, 0); channel as i64, 0, 0);
@ -140,16 +141,17 @@ pub fn log(timestamp: i64, data: &[u8]) {
for i in 0..data.len() { for i in 0..data.len() {
word <<= 8; word <<= 8;
word |= data[i] as u32; word |= data[i] as u32;
if i % 4 == 0 { if i % 4 == 3 {
rtio_o_data_write(0, word); rtio_o_data_write(0, word);
csr::rtio::o_we_write(1); csr::rtio::o_we_write(1);
word = 0; word = 0;
} }
} }
word <<= 8; if word != 0 {
rtio_o_data_write(0, word); rtio_o_data_write(0, word);
csr::rtio::o_we_write(1); csr::rtio::o_we_write(1);
}
} }
} }

View File

@ -1,10 +1,7 @@
#![feature(alloc, allocator_api)]
#![no_std] #![no_std]
extern crate alloc; use core::{ptr, mem, fmt};
use core::alloc::{GlobalAlloc, Layout};
use core::{mem, fmt};
use alloc::allocator::{Layout, AllocErr, Alloc};
// The minimum alignment guaranteed by the architecture. // The minimum alignment guaranteed by the architecture.
const MIN_ALIGN: usize = 4; const MIN_ALIGN: usize = 4;
@ -42,10 +39,10 @@ impl ListAlloc {
} }
} }
unsafe impl<'a> Alloc for &'a ListAlloc { unsafe impl GlobalAlloc for ListAlloc {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() > MIN_ALIGN { if layout.align() > MIN_ALIGN {
return Err(AllocErr::Unsupported { details: "alignment too large" }) panic!("cannot allocate with alignment {}", layout.align())
} }
let header_size = mem::size_of::<Header>(); let header_size = mem::size_of::<Header>();
@ -83,7 +80,7 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
if (*curr).size >= size { if (*curr).size >= size {
(*curr).magic = MAGIC_BUSY; (*curr).magic = MAGIC_BUSY;
return Ok(curr.offset(1) as *mut u8) return curr.offset(1) as *mut u8
} }
}, },
_ => panic!("heap corruption detected at {:p}", curr) _ => panic!("heap corruption detected at {:p}", curr)
@ -92,31 +89,25 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
curr = (*curr).next; curr = (*curr).next;
} }
Err(AllocErr::Exhausted { request: layout }) ptr::null_mut()
} }
unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) { unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
let curr = (ptr as *mut Header).offset(-1); let curr = (ptr as *mut Header).offset(-1);
if (*curr).magic != MAGIC_BUSY { if (*curr).magic != MAGIC_BUSY {
panic!("heap corruption detected at {:p}", curr) panic!("heap corruption detected at {:p}", curr)
} }
(*curr).magic = MAGIC_FREE; (*curr).magic = MAGIC_FREE;
} }
fn oom(&mut self, err: AllocErr) -> ! {
panic!("cannot allocate: {:?}", err)
}
} }
impl ListAlloc { impl fmt::Display for ListAlloc {
pub fn debug_dump(&self, f: &mut fmt::Write) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe { unsafe {
let mut total_busy = 0; let mut total_busy = 0;
let mut total_idle = 0; let mut total_idle = 0;
let mut total_meta = 0; let mut total_meta = 0;
write!(f, "Heap view:\n")?;
let mut curr = self.root; let mut curr = self.root;
while !curr.is_null() { while !curr.is_null() {
total_meta += mem::size_of::<Header>(); total_meta += mem::size_of::<Header>();

View File

@ -3,16 +3,16 @@
extern crate alloc; extern crate alloc;
use alloc::allocator::{Layout, AllocErr, Alloc}; use core::alloc::{Layout, GlobalAlloc};
pub struct StubAlloc; pub struct StubAlloc;
unsafe impl<'a> Alloc for &'a StubAlloc { unsafe impl GlobalAlloc for StubAlloc {
unsafe fn alloc(&mut self, _layout: Layout) -> Result<*mut u8, AllocErr> { unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
unimplemented!() unimplemented!()
} }
unsafe fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) { unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
unimplemented!() unimplemented!()
} }
} }

View File

@ -0,0 +1,8 @@
[package]
authors = ["M-Labs"]
name = "backtrace_artiq"
version = "0.0.0"
[lib]
name = "backtrace_artiq"
path = "lib.rs"

View File

@ -0,0 +1,30 @@
#![feature(libc, panic_unwind)]
#![no_std]
extern crate unwind;
extern crate libc;
use unwind as uw;
use libc::c_void;
pub fn backtrace<F>(mut f: F) -> Result<(), uw::_Unwind_Reason_Code>
where F: FnMut(usize) -> ()
{
extern fn trace<F>(context: *mut uw::_Unwind_Context, arg: *mut c_void)
-> uw::_Unwind_Reason_Code
where F: FnMut(usize) -> ()
{
unsafe {
let step_fn = &mut *(arg as *mut F);
step_fn(uw::_Unwind_GetIP(context));
uw::_URC_NO_REASON
}
}
unsafe {
match uw::_Unwind_Backtrace(trace::<F>, &mut f as *mut _ as *mut c_void) {
uw::_URC_NO_REASON => Ok(()),
err => Err(err)
}
}
}

View File

@ -9,6 +9,7 @@ name = "board"
path = "lib.rs" path = "lib.rs"
[dependencies] [dependencies]
byteorder = { version = "1.0", default-features = false }
log = { version = "0.3", default-features = false } log = { version = "0.3", default-features = false }
[features] [features]

View File

@ -0,0 +1,237 @@
#[cfg(has_spiflash)]
mod imp {
use core::str;
use byteorder::{ByteOrder, BigEndian};
use cache;
use spiflash;
// One flash sector immediately before the firmware.
const ADDR: usize = ::mem::FLASH_BOOT_ADDRESS - spiflash::SECTOR_SIZE;
const SIZE: usize = spiflash::SECTOR_SIZE;
mod lock {
use core::slice;
use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
static LOCKED: AtomicUsize = ATOMIC_USIZE_INIT;
pub struct Lock;
impl Lock {
pub fn take() -> Result<Lock, ()> {
if LOCKED.swap(1, Ordering::SeqCst) != 0 {
Err(()) // already locked
} else {
Ok(Lock) // locked now
}
}
pub fn data(&self) -> &'static [u8] {
unsafe { slice::from_raw_parts(super::ADDR as *const u8, super::SIZE) }
}
}
impl Drop for Lock {
fn drop(&mut self) {
LOCKED.store(0, Ordering::SeqCst)
}
}
}
use self::lock::Lock;
#[derive(Clone)]
struct Iter<'a> {
data: &'a [u8],
offset: usize
}
impl<'a> Iter<'a> {
fn new(data: &'a [u8]) -> Iter<'a> {
Iter { data: data, offset: 0 }
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Result<(&'a [u8], &'a [u8]), ()>;
fn next(&mut self) -> Option<Self::Item> {
let data = &self.data[self.offset..];
if data.len() < 4 {
error!("offset {}: truncated record", self.offset);
return Some(Err(()))
}
let record_size = BigEndian::read_u32(data) as usize;
if record_size == !0 /* all ones; erased flash */ {
return None
} else if record_size < 4 || record_size > data.len() {
error!("offset {}: invalid record size {}", self.offset, record_size);
return Some(Err(()))
}
let record_body = &data[4..record_size];
match record_body.iter().position(|&x| x == 0) {
None => {
error!("offset {}: missing separator", self.offset);
Some(Err(()))
}
Some(pos) => {
self.offset += record_size;
let (key, zero_and_value) = record_body.split_at(pos);
Some(Ok((key, &zero_and_value[1..])))
}
}
}
}
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(key: &str, f: F) -> R {
f(Lock::take().and_then(|lock| {
let mut iter = Iter::new(lock.data());
let mut value = &[][..];
while let Some(result) = iter.next() {
let (record_key, record_value) = result?;
if key.as_bytes() == record_key {
// last write wins
value = record_value
}
}
Ok(value)
}))
}
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(key: &str, f: F) -> R {
read(key, |result| {
f(result.and_then(|value| str::from_utf8(value).map_err(|_| ())))
})
}
unsafe fn append_at<'a>(mut data: &'a [u8], key: &[u8], value: &[u8]) -> Result<&'a [u8], ()> {
let record_size = 4 + key.len() + 1 + value.len();
if data.len() < record_size {
return Err(())
}
let mut record_size_bytes = [0u8; 4];
BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32);
spiflash::write(data.as_ptr() as usize, &record_size_bytes[..]);
data = &data[record_size_bytes.len()..];
spiflash::write(data.as_ptr() as usize, key);
data = &data[key.len()..];
spiflash::write(data.as_ptr() as usize, &[0]);
data = &data[1..];
spiflash::write(data.as_ptr() as usize, value);
data = &data[value.len()..];
cache::flush_l2_cache();
Ok(data)
}
fn compact() -> Result<(), ()> {
let lock = Lock::take()?;
static mut OLD_DATA: [u8; SIZE] = [0; SIZE];
let old_data = unsafe {
OLD_DATA.copy_from_slice(lock.data());
&OLD_DATA[..]
};
let mut data = lock.data();
unsafe { spiflash::erase_sector(data.as_ptr() as usize) };
// This is worst-case quadratic, but we're limited by a small SPI flash sector size,
// so it does not really matter.
let mut iter = Iter::new(old_data);
'iter: while let Some(result) = iter.next() {
let (key, mut value) = result?;
if value.is_empty() {
// This is a removed entry, ignore it.
continue
}
let mut next_iter = iter.clone();
while let Some(next_result) = next_iter.next() {
let (next_key, _) = next_result?;
if key == next_key {
// There's another entry that overwrites this one, ignore this one.
continue 'iter
}
}
data = unsafe { append_at(data, key, value)? };
}
Ok(())
}
fn append(key: &str, value: &[u8]) -> Result<(), ()> {
let lock = Lock::take()?;
let free = {
let mut iter = Iter::new(lock.data());
while let Some(result) = iter.next() {
let _ = result?;
}
&iter.data[iter.offset..]
};
unsafe { append_at(free, key.as_bytes(), value)? };
Ok(())
}
pub fn write(key: &str, value: &[u8]) -> Result<(), ()> {
match append(key, value) {
Ok(()) => (),
Err(()) => {
compact()?;
append(key, value)?;
}
}
Ok(())
}
pub fn remove(key: &str) -> Result<(), ()> {
write(key, &[])
}
pub fn erase() -> Result<(), ()> {
let lock = Lock::take()?;
unsafe { spiflash::erase_sector(lock.data().as_ptr() as usize) };
cache::flush_l2_cache();
Ok(())
}
}
#[cfg(not(has_spiflash))]
mod imp {
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(_key: &str, f: F) -> R {
f(Err(()))
}
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(_key: &str, f: F) -> R {
f(Err(()))
}
pub fn write(_key: &str, _value: &[u8]) -> Result<(), ()> {
Err(())
}
pub fn remove(_key: &str) -> Result<(), ()> {
Err(())
}
pub fn erase() -> Result<(), ()> {
Err(())
}
}
pub use self::imp::*;

View File

@ -1,10 +1,11 @@
#![feature(asm, lang_items)] #![feature(asm, lang_items)]
#![no_std] #![no_std]
extern crate byteorder;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use core::{cmp, ptr, str}; use core::{cmp, str};
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs")); include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs"));
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs")); include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));
@ -18,6 +19,7 @@ pub mod uart_console;
#[cfg(has_spiflash)] #[cfg(has_spiflash)]
pub mod spiflash; pub mod spiflash;
pub mod config;
pub mod i2c; pub mod i2c;
pub mod spi; pub mod spi;
@ -43,11 +45,13 @@ pub use uart_console::Console;
pub fn ident(buf: &mut [u8]) -> &str { pub fn ident(buf: &mut [u8]) -> &str {
unsafe { unsafe {
let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE); csr::identifier::address_write(0);
let len = cmp::min(len as usize, buf.len()); let len = csr::identifier::data_read();
let len = cmp::min(len, buf.len() as u8);
for i in 0..len { for i in 0..len {
buf[i] = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE.offset(1 + i as isize)) as u8 csr::identifier::address_write(1 + i);
buf[i as usize] = csr::identifier::data_read();
} }
str::from_utf8_unchecked(&buf[..len]) str::from_utf8_unchecked(&buf[..len as usize])
} }
} }

View File

@ -3,6 +3,11 @@
use core::cmp; use core::cmp;
use csr; use csr;
pub const SECTOR_SIZE: usize = csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize;
pub const PAGE_SIZE: usize = csr::CONFIG_SPIFLASH_PAGE_SIZE as usize;
const PAGE_MASK: usize = PAGE_SIZE - 1;
const CMD_PP: u8 = 0x02; const CMD_PP: u8 = 0x02;
const CMD_WRDI: u8 = 0x04; const CMD_WRDI: u8 = 0x04;
const CMD_RDSR: u8 = 0x05; const CMD_RDSR: u8 = 0x05;
@ -15,28 +20,24 @@ const PIN_DQ_I: u8 = 1 << 3;
const SR_WIP: u8 = 1; const SR_WIP: u8 = 1;
fn write_byte(mut byte: u8) { unsafe fn write_byte(mut byte: u8) {
unsafe { csr::spiflash::bitbang_write(0);
csr::spiflash::bitbang_write(0); for _ in 0..8 {
for _ in 0..8 { csr::spiflash::bitbang_write((byte & 0x80) >> 7);
csr::spiflash::bitbang_write((byte & 0x80) >> 7); csr::spiflash::bitbang_write((byte & 0x80) >> 7 | PIN_CLK);
csr::spiflash::bitbang_write((byte & 0x80) >> 7 | PIN_CLK); byte <<= 1;
byte <<= 1;
}
csr::spiflash::bitbang_write(0);
} }
csr::spiflash::bitbang_write(0);
} }
fn write_addr(mut addr: usize) { unsafe fn write_addr(mut addr: usize) {
unsafe { csr::spiflash::bitbang_write(0);
csr::spiflash::bitbang_write(0); for _ in 0..24 {
for _ in 0..24 { csr::spiflash::bitbang_write(((addr & 0x800000) >> 23) as u8);
csr::spiflash::bitbang_write(((addr & 0x800000) >> 23) as u8); csr::spiflash::bitbang_write(((addr & 0x800000) >> 23) as u8 | PIN_CLK);
csr::spiflash::bitbang_write(((addr & 0x800000) >> 23) as u8 | PIN_CLK); addr <<= 1;
addr <<= 1;
}
csr::spiflash::bitbang_write(0);
} }
csr::spiflash::bitbang_write(0);
} }
fn wait_until_ready() { fn wait_until_ready() {
@ -59,54 +60,47 @@ fn wait_until_ready() {
} }
} }
pub fn erase_sector(addr: usize) { pub unsafe fn erase_sector(addr: usize) {
unsafe { let sector_addr = addr & !(csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize - 1);
let sector_addr = addr & !(csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize - 1);
csr::spiflash::bitbang_en_write(1); csr::spiflash::bitbang_en_write(1);
wait_until_ready(); wait_until_ready();
write_byte(CMD_WREN); write_byte(CMD_WREN);
csr::spiflash::bitbang_write(PIN_CS_N); csr::spiflash::bitbang_write(PIN_CS_N);
write_byte(CMD_SE); write_byte(CMD_SE);
write_addr(sector_addr); write_addr(sector_addr);
csr::spiflash::bitbang_write(PIN_CS_N); csr::spiflash::bitbang_write(PIN_CS_N);
wait_until_ready(); wait_until_ready();
csr::spiflash::bitbang_en_write(0); csr::spiflash::bitbang_en_write(0);
}
} }
fn write_page(addr: usize, data: &[u8]) { unsafe fn write_page(addr: usize, data: &[u8]) {
unsafe { csr::spiflash::bitbang_en_write(1);
csr::spiflash::bitbang_en_write(1);
wait_until_ready(); wait_until_ready();
write_byte(CMD_WREN); write_byte(CMD_WREN);
csr::spiflash::bitbang_write(PIN_CS_N); csr::spiflash::bitbang_write(PIN_CS_N);
write_byte(CMD_PP); write_byte(CMD_PP);
write_addr(addr); write_addr(addr);
for &byte in data { for &byte in data {
write_byte(byte) write_byte(byte)
}
csr::spiflash::bitbang_write(PIN_CS_N);
csr::spiflash::bitbang_write(0);
wait_until_ready();
csr::spiflash::bitbang_en_write(0);
} }
csr::spiflash::bitbang_write(PIN_CS_N);
csr::spiflash::bitbang_write(0);
wait_until_ready();
csr::spiflash::bitbang_en_write(0);
} }
const PAGE_SIZE: usize = csr::CONFIG_SPIFLASH_PAGE_SIZE as usize; pub unsafe fn write(mut addr: usize, mut data: &[u8]) {
const PAGE_MASK: usize = PAGE_SIZE - 1;
pub fn write(mut addr: usize, mut data: &[u8]) {
if addr & PAGE_MASK != 0 { if addr & PAGE_MASK != 0 {
let size = cmp::min((PAGE_SIZE - (addr & PAGE_MASK)) as usize, data.len()); let size = cmp::min((PAGE_SIZE - (addr & PAGE_MASK)) as usize, data.len());
write_page(addr, &data[..size]); write_page(addr, &data[..size]);

View File

@ -0,0 +1,11 @@
[package]
authors = ["M-Labs"]
name = "eh"
version = "0.0.0"
[lib]
name = "eh"
path = "lib.rs"
[dependencies]
cslice = { version = "0.3" }

View File

@ -0,0 +1,243 @@
#![allow(non_upper_case_globals, dead_code)]
use core::{ptr, mem};
use cslice::CSlice;
const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;
const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;
const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;
const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}
impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: ptr }
}
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are
// telling the backend to generate "misalignment-safe" code.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = ptr::read_unaligned(self.ptr as *const T);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result
}
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
unsafe fn read_uleb128(&mut self) -> u64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
unsafe fn read_sleb128(&mut self) -> i64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}
debug_assert!(encoding != DW_EH_PE_omit);
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
_ => panic!(),
};
if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}
result
}
}
fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}
match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}
pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}
pub unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
exn_name: CSlice<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};
let ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
let class_info;
if ttype_encoding != DW_EH_PE_omit {
let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();
if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}
if cs_lpad == 0 {
return EHAction::None
}
let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}
let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);
if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);
if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}
if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}
if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}
return EHAction::None
}
// the function has a personality but no landing pads; this is fine
EHAction::None
}

View File

@ -0,0 +1,88 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This is the Rust personality function, adapted for use in ARTIQ. We never actually panic
// from Rust or recover from Rust exceptions (there's nothing to catch the panics), but we
// need a personality function to step back through Rust frames in order to make a backtrace.
//
// By design, this personality function is only ever called in the search phase, although
// to keep things simple and close to upstream, it is not modified
#![allow(private_no_mangle_fns)]
use unwind as uw;
use libc::{c_int, uintptr_t};
use cslice::AsCSlice;
use dwarf::{self, EHAction};
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
// and TargetLowering::getExceptionSelectorRegister() for each architecture,
// then mapped to DWARF register numbers via register definition tables
// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
// See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
#[cfg(target_arch = "x86")]
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
// The following code is based on GCC's C and C++ personality routines. For reference, see:
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
#[lang = "eh_personality"]
#[no_mangle]
#[allow(unused)]
unsafe extern "C" fn rust_eh_personality(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
if version != 1 {
return uw::_URC_FATAL_PHASE1_ERROR;
}
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
};
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
-> Result<EHAction, ()>
{
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let func = uw::_Unwind_GetRegionStart(context);
let ip = uw::_Unwind_GetIP(context);
Ok(dwarf::find_eh_action(lsda, func, ip, [].as_c_slice()))
}

View File

@ -0,0 +1,9 @@
#![feature(lang_items, panic_unwind, libc, unwind_attributes)]
#![no_std]
extern crate cslice;
extern crate unwind;
extern crate libc;
pub mod dwarf;
// pub mod eh_rust;

View File

@ -8,6 +8,6 @@ name = "logger_artiq"
path = "lib.rs" path = "lib.rs"
[dependencies] [dependencies]
log = { version = "0.3", default-features = false, features = [] } log = { version = "0.4", default-features = false }
log_buffer = { version = "1.0" } log_buffer = { version = "1.2" }
board = { path = "../libboard" } board = { path = "../libboard" }

View File

@ -2,19 +2,49 @@
extern crate log; extern crate log;
extern crate log_buffer; extern crate log_buffer;
#[macro_use]
extern crate board; extern crate board;
use core::{mem, ptr}; use core::cell::{Cell, RefCell, RefMut};
use core::cell::{Cell, RefCell};
use core::fmt::Write; use core::fmt::Write;
use log::{Log, LogMetadata, LogRecord, LogLevelFilter, MaxLogLevelFilter}; use log::{Log, LevelFilter};
use log_buffer::LogBuffer; use log_buffer::LogBuffer;
use board::{Console, clock}; use board::clock;
pub struct LogBufferRef<'a> {
buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>,
old_log_level: LevelFilter
}
impl<'a> LogBufferRef<'a> {
fn new(buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>) -> LogBufferRef<'a> {
let old_log_level = log::max_level();
log::set_max_level(LevelFilter::Off);
LogBufferRef { buffer, old_log_level }
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn clear(&mut self) {
self.buffer.clear()
}
pub fn extract(&mut self) -> &str {
self.buffer.extract()
}
}
impl<'a> Drop for LogBufferRef<'a> {
fn drop(&mut self) {
log::set_max_level(self.old_log_level)
}
}
pub struct BufferLogger { pub struct BufferLogger {
buffer: RefCell<LogBuffer<&'static mut [u8]>>, buffer: RefCell<LogBuffer<&'static mut [u8]>>,
filter: RefCell<Option<MaxLogLevelFilter>>, uart_filter: Cell<LevelFilter>
uart_filter: Cell<LogLevelFilter>
} }
static mut LOGGER: *const BufferLogger = 0 as *const _; static mut LOGGER: *const BufferLogger = 0 as *const _;
@ -23,71 +53,36 @@ impl BufferLogger {
pub fn new(buffer: &'static mut [u8]) -> BufferLogger { pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
BufferLogger { BufferLogger {
buffer: RefCell::new(LogBuffer::new(buffer)), buffer: RefCell::new(LogBuffer::new(buffer)),
filter: RefCell::new(None), uart_filter: Cell::new(LevelFilter::Info),
uart_filter: Cell::new(LogLevelFilter::Info),
} }
} }
pub fn register<F: FnOnce()>(&self, f: F) { pub fn register<F: FnOnce()>(&self, f: F) {
// log::set_logger_raw captures a pointer to ourselves, so we must prevent
// ourselves from being moved or dropped after that function is called (and
// before log::shutdown_logger_raw is called).
unsafe { unsafe {
log::set_logger_raw(|max_log_level| {
max_log_level.set(LogLevelFilter::Info);
*self.filter.borrow_mut() = Some(max_log_level);
self as *const Log
}).expect("global logger can only be initialized once");
LOGGER = self; LOGGER = self;
log::set_logger(&*LOGGER)
.expect("global logger can only be initialized once");
} }
log::set_max_level(LevelFilter::Info);
f(); f();
log::shutdown_logger_raw().unwrap();
unsafe {
LOGGER = ptr::null();
}
} }
pub fn with<R, F: FnOnce(&BufferLogger) -> R>(f: F) -> R { pub fn with<R, F: FnOnce(&BufferLogger) -> R>(f: F) -> R {
f(unsafe { mem::transmute::<*const BufferLogger, &BufferLogger>(LOGGER) }) f(unsafe { &*LOGGER })
} }
pub fn clear(&self) { pub fn buffer<'a>(&'a self) -> Result<LogBufferRef<'a>, ()> {
self.buffer.borrow_mut().clear() self.buffer
.try_borrow_mut()
.map(LogBufferRef::new)
.map_err(|_| ())
} }
pub fn is_empty(&self) -> bool { pub fn uart_log_level(&self) -> LevelFilter {
self.buffer.borrow_mut().extract().len() == 0
}
pub fn extract<R, F: FnOnce(&str) -> R>(&self, f: F) -> R {
let old_log_level = self.max_log_level();
self.set_max_log_level(LogLevelFilter::Off);
let result = f(self.buffer.borrow_mut().extract());
self.set_max_log_level(old_log_level);
result
}
pub fn max_log_level(&self) -> LogLevelFilter {
self.filter
.borrow()
.as_ref()
.expect("register the logger before touching maximum log level")
.get()
}
pub fn set_max_log_level(&self, max_level: LogLevelFilter) {
self.filter
.borrow()
.as_ref()
.expect("register the logger before touching maximum log level")
.set(max_level)
}
pub fn uart_log_level(&self) -> LogLevelFilter {
self.uart_filter.get() self.uart_filter.get()
} }
pub fn set_uart_log_level(&self, max_level: LogLevelFilter) { pub fn set_uart_log_level(&self, max_level: LevelFilter) {
self.uart_filter.set(max_level) self.uart_filter.set(max_level)
} }
} }
@ -96,25 +91,28 @@ impl BufferLogger {
unsafe impl Sync for BufferLogger {} unsafe impl Sync for BufferLogger {}
impl Log for BufferLogger { impl Log for BufferLogger {
fn enabled(&self, _metadata: &LogMetadata) -> bool { fn enabled(&self, _metadata: &log::Metadata) -> bool {
true true
} }
fn log(&self, record: &LogRecord) { fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) { if self.enabled(record.metadata()) {
let timestamp = clock::get_us(); let timestamp = clock::get_us();
let seconds = timestamp / 1_000_000; let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000; let micros = timestamp % 1_000_000;
writeln!(self.buffer.borrow_mut(), if let Ok(mut buffer) = self.buffer.try_borrow_mut() {
"[{:6}.{:06}s] {:>5}({}): {}", seconds, micros, writeln!(buffer, "[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
record.level(), record.target(), record.args()).unwrap(); record.level(), record.target(), record.args()).unwrap();
}
if record.level() <= self.uart_filter.get() { if record.level() <= self.uart_filter.get() {
writeln!(Console, println!("[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
"[{:6}.{:06}s] {:>5}({}): {}", seconds, micros, record.level(), record.target(), record.args());
record.level(), record.target(), record.args()).unwrap();
} }
} }
} }
fn flush(&self) {
}
} }

View File

@ -10,6 +10,6 @@ path = "lib.rs"
[dependencies] [dependencies]
byteorder = { version = "1.0", default-features = false } byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" } cslice = { version = "0.3" }
log = { version = "0.3", default-features = false, optional = true } log = { version = "0.4", default-features = false, optional = true }
std_artiq = { path = "../libstd_artiq", features = ["alloc"] } std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
dyld = { path = "../libdyld" } dyld = { path = "../libdyld" }

View File

@ -74,6 +74,7 @@ pub enum Message<'a> {
}, },
RpcRecvRequest(*mut ()), RpcRecvRequest(*mut ()),
RpcRecvReply(Result<usize, Exception<'a>>), RpcRecvReply(Result<usize, Exception<'a>>),
RpcFlush,
CacheGetRequest { key: &'a str }, CacheGetRequest { key: &'a str },
CacheGetReply { value: &'static [i32] }, CacheGetReply { value: &'static [i32] },

View File

@ -2,7 +2,7 @@ use std::vec::Vec;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use {ReadExt, WriteExt}; use {ReadExt, WriteExt};
#[cfg(feature = "log")] #[cfg(feature = "log")]
use log::LogLevelFilter; use log;
#[derive(Debug)] #[derive(Debug)]
pub enum Request { pub enum Request {
@ -10,9 +10,9 @@ pub enum Request {
ClearLog, ClearLog,
PullLog, PullLog,
#[cfg(feature = "log")] #[cfg(feature = "log")]
SetLogFilter(LogLevelFilter), SetLogFilter(log::LevelFilter),
#[cfg(feature = "log")] #[cfg(feature = "log")]
SetUartLogFilter(LogLevelFilter), SetUartLogFilter(log::LevelFilter),
Hotswap(Vec<u8>), Hotswap(Vec<u8>),
Reboot, Reboot,
@ -29,14 +29,14 @@ pub enum Reply<'a> {
impl Request { impl Request {
pub fn read_from(reader: &mut Read) -> io::Result<Request> { pub fn read_from(reader: &mut Read) -> io::Result<Request> {
#[cfg(feature = "log")] #[cfg(feature = "log")]
fn read_log_level_filter(reader: &mut Read) -> io::Result<LogLevelFilter> { fn read_log_level_filter(reader: &mut Read) -> io::Result<log::LevelFilter> {
Ok(match reader.read_u8()? { Ok(match reader.read_u8()? {
0 => LogLevelFilter::Off, 0 => log::LevelFilter::Off,
1 => LogLevelFilter::Error, 1 => log::LevelFilter::Error,
2 => LogLevelFilter::Warn, 2 => log::LevelFilter::Warn,
3 => LogLevelFilter::Info, 3 => log::LevelFilter::Info,
4 => LogLevelFilter::Debug, 4 => log::LevelFilter::Debug,
5 => LogLevelFilter::Trace, 5 => log::LevelFilter::Trace,
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, _ => return Err(io::Error::new(io::ErrorKind::InvalidData,
"invalid log level")) "invalid log level"))
}) })

View File

@ -140,11 +140,11 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::
Ok(()) Ok(())
} }
Tag::Keyword(it) => { Tag::Keyword(it) => {
struct Keyword<'a> { name: CSlice<'a, u8>, contents: () }; struct Keyword<'a> { name: CSlice<'a, u8> };
consume_value!(Keyword, |ptr| { consume_value!(Keyword, |ptr| {
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?; writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");
let mut data = &(*ptr).contents as *const (); let mut data = ptr.offset(1) as *const ();
send_value(writer, tag, &mut data) send_value(writer, tag, &mut data)
}) })
// Tag::Keyword never appears in composite types, so we don't have // Tag::Keyword never appears in composite types, so we don't have
@ -243,8 +243,8 @@ mod tag {
Tag::Int64 => 8, Tag::Int64 => 8,
Tag::Float64 => 8, Tag::Float64 => 8,
Tag::String => 4, Tag::String => 4,
Tag::Bytes => 4, Tag::Bytes => 8,
Tag::ByteArray => 4, Tag::ByteArray => 8,
Tag::Tuple(it, arity) => { Tag::Tuple(it, arity) => {
let mut size = 0; let mut size = 0;
for _ in 0..arity { for _ in 0..arity {

View File

@ -1,19 +1,17 @@
#![feature(lang_items, asm, alloc, needs_panic_runtime, #![feature(lang_items, asm, alloc, needs_panic_runtime, use_extern_macros,
unicode, raw, int_error_internals, try_from, macro_reexport, unicode, raw, int_error_internals, try_from,
allow_internal_unstable, stmt_expr_attributes, str_internals)] allow_internal_unstable, stmt_expr_attributes, str_internals)]
#![no_std] #![no_std]
#![needs_panic_runtime] #![needs_panic_runtime]
extern crate std_unicode;
#[macro_use] #[macro_use]
#[macro_reexport(vec, format)]
extern crate alloc; extern crate alloc;
pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num, pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num,
ops, option, ptr, result, sync, ops, option, ptr, result, sync,
char, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize, f32, f64}; char, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize, f32, f64};
pub use alloc::{arc, rc, raw_vec}; pub use alloc::{arc, rc, raw_vec};
pub use alloc::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, linked_list, slice, pub use alloc::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, format, linked_list, slice,
str, string, vec, vec_deque}; str, string, vec, vec_deque};
pub mod prelude { pub mod prelude {

View File

@ -15,28 +15,25 @@ build_artiq = { path = "../libbuild_artiq" }
[dependencies] [dependencies]
byteorder = { version = "1.0", default-features = false } byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" } cslice = { version = "0.3" }
log = { version = "0.3", default-features = false } log = { version = "0.4", default-features = false }
managed = { version = "= 0.7.0", default-features = false, features = ["alloc", "map"] }
alloc_list = { path = "../liballoc_list" } alloc_list = { path = "../liballoc_list" }
std_artiq = { path = "../libstd_artiq", features = ["alloc", "io_error_alloc"] } std_artiq = { path = "../libstd_artiq", features = ["alloc", "io_error_alloc"] }
logger_artiq = { path = "../liblogger_artiq" } logger_artiq = { path = "../liblogger_artiq" }
backtrace_artiq = { path = "../libbacktrace_artiq" }
board = { path = "../libboard", features = ["uart_console"] } board = { path = "../libboard", features = ["uart_console"] }
proto = { path = "../libproto", features = ["log"] } proto = { path = "../libproto", features = ["log"] }
amp = { path = "../libamp" } amp = { path = "../libamp" }
drtioaux = { path = "../libdrtioaux" } drtioaux = { path = "../libdrtioaux" }
[dependencies.compiler_builtins]
git = "https://github.com/rust-lang-nursery/compiler-builtins"
rev = "631b568"
features = ["mem"]
[dependencies.fringe] [dependencies.fringe]
git = "https://github.com/m-labs/libfringe" git = "https://github.com/m-labs/libfringe"
rev = "bd23494" rev = "b8a6d8f"
default-features = false default-features = false
features = ["alloc"] features = ["alloc"]
[dependencies.smoltcp] [dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp" git = "https://github.com/m-labs/smoltcp"
rev = "6f5ae33" rev = "92e970b" # NB: also change in libboard_misoc/Cargo.toml
default-features = false default-features = false
features = ["alloc", "log"] features = ["rust-1.28", "alloc", "log", "proto-ipv4", "socket-tcp"]

View File

@ -1,32 +1,30 @@
include ../include/generated/variables.mak include ../include/generated/variables.mak
include $(MISOC_DIRECTORY)/software/common.mak include $(MISOC_DIRECTORY)/software/common.mak
LDFLAGS += -L../libbase CFLAGS += \
-I$(LIBUNWIND_DIRECTORY) \
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include
RUSTFLAGS += -Cpanic=abort LDFLAGS += -L../libbase \
-L../libunwind
all: runtime.bin runtime.fbi RUSTFLAGS += -Cpanic=unwind
all:: runtime.bin runtime.fbi
.PHONY: $(RUSTOUT)/libruntime.a .PHONY: $(RUSTOUT)/libruntime.a
$(RUSTOUT)/libruntime.a: $(RUSTOUT)/libruntime.a:
$(cargo) --manifest-path $(RUNTIME_DIRECTORY)/Cargo.toml $(cargo) --manifest-path $(RUNTIME_DIRECTORY)/Cargo.toml
runtime.elf: $(RUSTOUT)/libruntime.a ksupport_data.o runtime.elf: $(RUSTOUT)/libruntime.a ksupport_data.o
$(LD) $(LDFLAGS) -T $(RUNTIME_DIRECTORY)/runtime.ld -o $@ $^ $(link) -T $(RUNTIME_DIRECTORY)/runtime.ld \
@chmod -x $@ -lunwind-bare
ksupport_data.o: ../ksupport/ksupport.elf ksupport_data.o: ../ksupport/ksupport.elf
$(LD) -r -b binary -o $@ $< $(LD) -r -b binary -o $@ $<
%.bin: %.elf %.bin: %.elf
$(OBJCOPY) -O binary $< $@ $(objcopy) -O binary
@chmod -x $@
%.fbi: %.bin %.fbi: %.bin
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< $(mscimg) -f
clean:
$(RM) *.o runtime.elf runtime.bin runtime.fbi
$(RM) -rf cargo
.PHONY: all clean

View File

@ -5,18 +5,13 @@ use analyzer_proto::*;
const BUFFER_SIZE: usize = 512 * 1024; const BUFFER_SIZE: usize = 512 * 1024;
// hack until https://github.com/rust-lang/rust/issues/33626 is fixed #[repr(align(64))]
#[repr(simd)]
struct Align64(u64, u64, u64, u64, u64, u64, u64, u64);
struct Buffer { struct Buffer {
data: [u8; BUFFER_SIZE], data: [u8; BUFFER_SIZE]
__alignment: [Align64; 0]
} }
static mut BUFFER: Buffer = Buffer { static mut BUFFER: Buffer = Buffer {
data: [0; BUFFER_SIZE], data: [0; BUFFER_SIZE]
__alignment: []
}; };
fn arm() { fn arm() {
@ -68,9 +63,6 @@ fn worker(stream: &mut TcpStream) -> io::Result<()> {
} }
pub fn thread(io: Io) { pub fn thread(io: Io) {
// verify that the hack above works
assert!(::core::mem::align_of::<Buffer>() == 64);
let listener = TcpListener::new(&io, 65535); let listener = TcpListener::new(&io, 65535);
listener.listen(1382).expect("analyzer: cannot listen"); listener.listen(1382).expect("analyzer: cannot listen");

View File

@ -1,192 +0,0 @@
use core::str;
use std::btree_map::BTreeMap;
use byteorder::{ByteOrder, BigEndian};
use board::{mem, csr, cache, spiflash};
const ADDR: usize = mem::FLASH_BOOT_ADDRESS + 0x80000 /* max runtime size */;
const SIZE: usize = csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize;
mod lock {
use core::slice;
use core::sync::atomic::{AtomicUsize, Ordering};
static LOCKED: AtomicUsize = AtomicUsize::new(0);
pub struct Lock;
impl Lock {
pub fn take() -> Result<Lock, ()> {
if LOCKED.swap(1, Ordering::SeqCst) != 0 {
Err(()) // already locked
} else {
Ok(Lock) // locked now
}
}
pub fn data(&self) -> &'static [u8] {
unsafe { slice::from_raw_parts(super::ADDR as *const u8, super::SIZE) }
}
}
impl Drop for Lock {
fn drop(&mut self) {
LOCKED.store(0, Ordering::SeqCst)
}
}
}
pub use self::lock::Lock;
struct Iter<'a> {
data: &'a [u8],
offset: usize
}
impl<'a> Iter<'a> {
fn new(data: &'a [u8]) -> Iter<'a> {
Iter { data: data, offset: 0 }
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Result<(&'a [u8], &'a [u8]), ()>;
fn next(&mut self) -> Option<Self::Item> {
let data = &self.data[self.offset..];
if data.len() < 4 {
error!("offset {}: truncated record", self.offset);
return Some(Err(()))
}
let record_size = BigEndian::read_u32(data) as usize;
if record_size < 4 {
error!("offset {}: invalid record size", self.offset);
return Some(Err(()))
}
if record_size == !0 /* all ones; erased flash */ {
return None
}
let record_body = &data[4..record_size];
match record_body.iter().position(|&x| x == 0) {
None => {
error!("offset {}: missing separator", self.offset);
Some(Err(()))
}
Some(pos) => {
self.offset += record_size;
let (key, zero_and_value) = record_body.split_at(pos);
Some(Ok((key, &zero_and_value[1..])))
}
}
}
}
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(key: &str, f: F) -> R {
f(Lock::take().and_then(|lock| {
let mut iter = Iter::new(lock.data());
let mut value = &[][..];
while let Some(result) = iter.next() {
let (record_key, record_value) = result?;
if key.as_bytes() == record_key {
// last write wins
value = record_value
}
}
Ok(value)
}))
}
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(key: &str, f: F) -> R {
read(key, |result| {
f(result.and_then(|value| str::from_utf8(value).map_err(|_| ())))
})
}
fn append_at(mut offset: usize, key: &[u8], value: &[u8]) -> Result<usize, ()> {
let record_size = 4 + key.len() + 1 + value.len();
if offset + record_size > SIZE {
return Err(())
}
let mut record_size_bytes = [0u8; 4];
BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32);
spiflash::write(ADDR + offset, &record_size_bytes[..]);
offset += record_size_bytes.len();
spiflash::write(ADDR + offset, key);
offset += key.len();
spiflash::write(ADDR + offset, &[0]);
offset += 1;
spiflash::write(ADDR + offset, value);
offset += value.len();
cache::flush_l2_cache();
Ok(offset)
}
fn compact() -> Result<(), ()> {
let lock = Lock::take()?;
let mut items = BTreeMap::new();
{
let mut iter = Iter::new(lock.data());
while let Some(result) = iter.next() {
let (key, value) = result?;
items.insert(key, value);
}
}
spiflash::erase_sector(ADDR);
cache::flush_l2_cache();
let mut offset = 0;
for (key, value) in items {
offset = append_at(offset, key, value)?;
}
Ok(())
}
fn append(key: &str, value: &[u8]) -> Result<(), ()> {
let lock = Lock::take()?;
let free_offset = {
let mut iter = Iter::new(lock.data());
while let Some(result) = iter.next() {
let _ = result?;
}
iter.offset
};
append_at(free_offset, key.as_bytes(), value)?;
Ok(())
}
pub fn write(key: &str, value: &[u8]) -> Result<(), ()> {
match append(key, value) {
Ok(()) => (),
Err(()) => {
compact()?;
append(key, value)?;
}
}
Ok(())
}
pub fn remove(key: &str) -> Result<(), ()> {
write(key, &[])
}
pub fn erase() -> Result<(), ()> {
let _lock = Lock::take()?;
spiflash::erase_sector(ADDR);
cache::flush_l2_cache();
Ok(())
}

View File

@ -1,19 +0,0 @@
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(_key: &str, f: F) -> R {
f(Err(()))
}
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(_key: &str, f: F) -> R {
f(Err(()))
}
pub fn write(_key: &str, _value: &[u8]) -> Result<(), ()> {
Err(())
}
pub fn remove(_key: &str) -> Result<(), ()> {
Err(())
}
pub fn erase() -> Result<(), ()> {
Err(())
}

View File

@ -1,88 +1,159 @@
use core::slice; use core::{slice, fmt};
use smoltcp::Result;
use smoltcp::time::Instant;
use smoltcp::phy::{self, DeviceCapabilities, Device};
use board::{csr, mem}; use board::{csr, mem};
use smoltcp::Error;
use smoltcp::phy::{DeviceLimits, Device};
const RX0_BASE: usize = mem::ETHMAC_BASE + 0x0000; const RX_SLOTS: usize = csr::ETHMAC_RX_SLOTS as usize;
const RX1_BASE: usize = mem::ETHMAC_BASE + 0x0800; const TX_SLOTS: usize = csr::ETHMAC_TX_SLOTS as usize;
const RX2_BASE: usize = mem::ETHMAC_BASE + 0x1000; const SLOT_SIZE: usize = csr::ETHMAC_SLOT_SIZE as usize;
const RX3_BASE: usize = mem::ETHMAC_BASE + 0x1800;
const TX0_BASE: usize = mem::ETHMAC_BASE + 0x2000;
const TX1_BASE: usize = mem::ETHMAC_BASE + 0x2800;
const TX2_BASE: usize = mem::ETHMAC_BASE + 0x3000;
const TX3_BASE: usize = mem::ETHMAC_BASE + 0x3800;
const RX_BUFFERS: [*mut u8; 4] = [RX0_BASE as *mut u8, RX1_BASE as *mut u8, fn next_rx_slot() -> Option<usize> {
RX2_BASE as *mut u8, RX3_BASE as *mut u8]; unsafe {
const TX_BUFFERS: [*mut u8; 4] = [TX0_BASE as *mut u8, TX1_BASE as *mut u8, if csr::ethmac::sram_writer_ev_pending_read() == 0 {
TX2_BASE as *mut u8, TX3_BASE as *mut u8]; None
} else {
pub struct EthernetDevice; Some(csr::ethmac::sram_writer_slot_read() as usize)
impl Device for EthernetDevice {
type RxBuffer = RxBuffer;
type TxBuffer = TxBuffer;
fn limits(&self) -> DeviceLimits {
let mut limits = DeviceLimits::default();
limits.max_transmission_unit = 1514;
limits.max_burst_size = Some(RX_BUFFERS.len());
limits
}
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer, Error> {
unsafe {
if csr::ethmac::sram_writer_ev_pending_read() != 0 {
let slot = csr::ethmac::sram_writer_slot_read();
let length = csr::ethmac::sram_writer_length_read();
Ok(RxBuffer(slice::from_raw_parts(RX_BUFFERS[slot as usize],
length as usize)))
} else {
Err(Error::Exhausted)
}
}
}
fn transmit(&mut self, _timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
unsafe {
if csr::ethmac::sram_reader_ready_read() != 0 {
let slot = csr::ethmac::sram_reader_slot_read();
let slot = (slot + 1) % (TX_BUFFERS.len() as u8);
csr::ethmac::sram_reader_slot_write(slot);
csr::ethmac::sram_reader_length_write(length as u16);
Ok(TxBuffer(slice::from_raw_parts_mut(TX_BUFFERS[slot as usize],
length as usize)))
} else {
Err(Error::Exhausted)
}
} }
} }
} }
pub struct RxBuffer(&'static [u8]); fn next_tx_slot() -> Option<usize> {
unsafe {
impl AsRef<[u8]> for RxBuffer { if csr::ethmac::sram_reader_ready_read() == 0 {
fn as_ref(&self) -> &[u8] { self.0 } None
} } else {
Some((csr::ethmac::sram_reader_slot_read() as usize + 1) % TX_SLOTS)
impl Drop for RxBuffer { }
fn drop(&mut self) {
unsafe { csr::ethmac::sram_writer_ev_pending_write(1) }
} }
} }
pub struct TxBuffer(&'static mut [u8]); fn rx_buffer(slot: usize) -> *const u8 {
debug_assert!(slot < RX_SLOTS);
impl AsRef<[u8]> for TxBuffer { (mem::ETHMAC_BASE + SLOT_SIZE * slot) as _
fn as_ref(&self) -> &[u8] { self.0 }
} }
impl AsMut<[u8]> for TxBuffer { fn tx_buffer(slot: usize) -> *mut u8 {
fn as_mut(&mut self) -> &mut [u8] { self.0 } debug_assert!(slot < TX_SLOTS);
(mem::ETHMAC_BASE + SLOT_SIZE * (RX_SLOTS + slot)) as _
} }
impl Drop for TxBuffer { pub struct EthernetDevice(());
fn drop(&mut self) {
unsafe { csr::ethmac::sram_reader_start_write(1) } impl EthernetDevice {
pub unsafe fn new() -> EthernetDevice {
EthernetDevice(())
}
}
impl<'a> Device<'a> for EthernetDevice {
type RxToken = EthernetRxSlot;
type TxToken = EthernetTxSlot;
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1514;
caps.max_burst_size = Some(RX_SLOTS);
caps
}
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
if let (Some(rx_slot), Some(tx_slot)) = (next_rx_slot(), next_tx_slot()) {
Some((EthernetRxSlot(rx_slot), EthernetTxSlot(tx_slot)))
} else {
None
}
}
fn transmit(&mut self) -> Option<Self::TxToken> {
if let Some(tx_slot) = next_tx_slot() {
Some(EthernetTxSlot(tx_slot))
} else {
None
}
}
}
pub struct EthernetRxSlot(usize);
impl phy::RxToken for EthernetRxSlot {
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&[u8]) -> Result<R>
{
unsafe {
let length = csr::ethmac::sram_writer_length_read() as usize;
let result = f(slice::from_raw_parts(rx_buffer(self.0), length));
csr::ethmac::sram_writer_ev_pending_write(1);
result
}
}
}
pub struct EthernetTxSlot(usize);
impl phy::TxToken for EthernetTxSlot {
fn consume<R, F>(self, _timestamp: Instant, length: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
{
debug_assert!(length < SLOT_SIZE);
unsafe {
let result = f(slice::from_raw_parts_mut(tx_buffer(self.0), length))?;
csr::ethmac::sram_reader_slot_write(self.0 as u8);
csr::ethmac::sram_reader_length_write(length as u16);
csr::ethmac::sram_reader_start_write(1);
Ok(result)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EthernetStatistics {
rx_preamble_errors: u32,
rx_crc_errors: u32,
rx_dropped: u32,
}
impl EthernetStatistics {
pub fn new() -> Self {
unsafe {
EthernetStatistics {
rx_preamble_errors: csr::ethmac::preamble_errors_read(),
rx_crc_errors: csr::ethmac::crc_errors_read(),
rx_dropped: csr::ethmac::sram_writer_errors_read(),
}
}
}
pub fn update(&mut self) -> Option<Self> {
let old = self.clone();
*self = Self::new();
let diff = EthernetStatistics {
rx_preamble_errors: self.rx_preamble_errors.wrapping_sub(old.rx_preamble_errors),
rx_crc_errors: self.rx_crc_errors.wrapping_sub(old.rx_crc_errors),
rx_dropped: self.rx_dropped.wrapping_sub(old.rx_dropped),
};
if diff == EthernetStatistics::default() {
None
} else {
Some(diff)
}
}
}
impl fmt::Display for EthernetStatistics {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.rx_preamble_errors > 0 {
write!(f, " rx preamble errors: {}", self.rx_preamble_errors)?
}
if self.rx_crc_errors > 0 {
write!(f, " rx crc errors: {}", self.rx_crc_errors)?
}
if self.rx_dropped > 0 {
write!(f, " rx dropped: {}", self.rx_dropped)?
}
Ok(())
} }
} }

View File

@ -319,7 +319,7 @@ pub fn process_kern_hwreq(io: &Io, request: &kern::Message) -> io::Result<bool>
match request { match request {
&kern::RtioInitRequest => { &kern::RtioInitRequest => {
info!("resetting RTIO"); info!("resetting RTIO");
rtio_mgt::init_core(); rtio_mgt::init_core(false);
kern_acknowledge() kern_acknowledge()
} }

View File

@ -1,7 +1,6 @@
#![no_std] #![no_std]
#![feature(compiler_builtins_lib, alloc, repr_simd, lang_items, const_fn, global_allocator)] #![feature(lang_items, alloc, panic_implementation, panic_info_message)]
extern crate compiler_builtins;
extern crate alloc; extern crate alloc;
extern crate cslice; extern crate cslice;
#[macro_use] #[macro_use]
@ -14,6 +13,7 @@ extern crate alloc_list;
#[macro_use] #[macro_use]
extern crate std_artiq as std; extern crate std_artiq as std;
extern crate logger_artiq; extern crate logger_artiq;
extern crate backtrace_artiq;
#[macro_use] #[macro_use]
extern crate board; extern crate board;
extern crate proto; extern crate proto;
@ -21,24 +21,12 @@ extern crate amp;
#[cfg(has_drtio)] #[cfg(has_drtio)]
extern crate drtioaux; extern crate drtioaux;
use std::boxed::Box; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::wire::{EthernetAddress, IpAddress};
use board::{config, irq, boot, clock};
use proto::{mgmt_proto, analyzer_proto, moninj_proto, rpc_proto, session_proto, kernel_proto}; use proto::{mgmt_proto, analyzer_proto, moninj_proto, rpc_proto, session_proto, kernel_proto};
use amp::{mailbox, rpc_queue}; use amp::{mailbox, rpc_queue};
macro_rules! borrow_mut {
($x:expr) => ({
match $x.try_borrow_mut() {
Ok(x) => x,
Err(_) => panic!("cannot borrow mutably at {}:{}", file!(), line!())
}
})
}
#[cfg(has_spiflash)]
mod config;
#[cfg(not(has_spiflash))]
#[path="config_dummy.rs"] mod config;
mod ethmac; mod ethmac;
mod rtio_mgt; mod rtio_mgt;
@ -95,7 +83,7 @@ fn startup() {
let protocol_addr; let protocol_addr;
match config::read_str("ip", |r| r?.parse()) { match config::read_str("ip", |r| r?.parse()) {
Err(()) | Ok(IpAddress::Unspecified) => { Err(()) => {
protocol_addr = IpAddress::v4(192, 168, 1, 50); protocol_addr = IpAddress::v4(192, 168, 1, 50);
info!("using default IP address {}", protocol_addr); info!("using default IP address {}", protocol_addr);
} }
@ -105,19 +93,36 @@ fn startup() {
} }
} }
// fn _net_trace_writer<U>(timestamp: u64, printer: smoltcp::wire::PrettyPrinter<U>) let net_device = unsafe { ethmac::EthernetDevice::new() };
// where U: smoltcp::wire::pretty_print::PrettyPrint {
// let seconds = timestamp / 1000;
// let micros = timestamp % 1000 * 1000;
// print!("\x1b[37m[{:6}.{:06}s]\n{}\x1b[0m", seconds, micros, printer)
// }
let net_device = ethmac::EthernetDevice; let net_device = {
// let net_device = smoltcp::phy::EthernetTracer::new(net_device, _net_trace_writer); use smoltcp::time::Instant;
let arp_cache = smoltcp::iface::SliceArpCache::new([Default::default(); 8]); use smoltcp::wire::PrettyPrinter;
let mut interface = smoltcp::iface::EthernetInterface::new( use smoltcp::wire::EthernetFrame;
Box::new(net_device), Box::new(arp_cache) as Box<smoltcp::iface::ArpCache>,
hardware_addr, [protocol_addr]); fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n",
timestamp.secs(), timestamp.millis(), printer)
}
fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
let net_trace_fn: fn(Instant, PrettyPrinter<EthernetFrame<&[u8]>>);
match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
Ok(true) => net_trace_fn = net_trace_writer,
_ => net_trace_fn = net_trace_silent
}
smoltcp::phy::EthernetTracer::new(net_device, net_trace_fn)
};
let neighbor_cache =
smoltcp::iface::NeighborCache::new(alloc::btree_map::BTreeMap::new());
let mut interface =
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
.neighbor_cache(neighbor_cache)
.ethernet_addr(hardware_addr)
.ip_addrs([IpCidr::new(protocol_addr, 0)])
.finalize();
let mut scheduler = sched::Scheduler::new(); let mut scheduler = sched::Scheduler::new();
let io = scheduler.io(); let io = scheduler.io();
@ -129,36 +134,38 @@ fn startup() {
#[cfg(has_rtio_analyzer)] #[cfg(has_rtio_analyzer)]
io.spawn(4096, analyzer::thread); io.spawn(4096, analyzer::thread);
match config::read_str("log_level", |r| r?.parse()) { match config::read_str("log_level", |r| r.map(|s| s.parse())) {
Err(()) => (), Ok(Ok(log_level_filter)) => {
Ok(log_level_filter) => {
info!("log level set to {} by `log_level` config key", info!("log level set to {} by `log_level` config key",
log_level_filter); log_level_filter);
logger_artiq::BufferLogger::with(|logger| log::set_max_level(log_level_filter);
logger.set_max_log_level(log_level_filter));
} }
_ => info!("log level set to INFO by default")
} }
match config::read_str("uart_log_level", |r| r.map(|s| s.parse())) {
match config::read_str("uart_log_level", |r| r?.parse()) { Ok(Ok(uart_log_level_filter)) => {
Err(()) => {
info!("UART log level set to INFO by default");
},
Ok(uart_log_level_filter) => {
info!("UART log level set to {} by `uart_log_level` config key", info!("UART log level set to {} by `uart_log_level` config key",
uart_log_level_filter); uart_log_level_filter);
logger_artiq::BufferLogger::with(|logger| logger_artiq::BufferLogger::with(|logger|
logger.set_uart_log_level(uart_log_level_filter)); logger.set_uart_log_level(uart_log_level_filter));
} }
_ => info!("UART log level set to INFO by default")
} }
loop { loop {
scheduler.run(); scheduler.run();
match interface.poll(&mut *borrow_mut!(scheduler.sockets()), {
board::clock::get_ms()) { let sockets = &mut *scheduler.sockets().borrow_mut();
Ok(_poll_at) => (), loop {
Err(smoltcp::Error::Unrecognized) => (), let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
Err(err) => warn!("network error: {}", err) match interface.poll(sockets, timestamp) {
Ok(true) => (),
Ok(false) => break,
Err(smoltcp::Error::Unrecognized) => (),
Err(err) => warn!("network error: {}", err)
}
}
} }
} }
} }
@ -189,28 +196,47 @@ pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32)
#[no_mangle] #[no_mangle]
pub extern fn abort() { pub extern fn abort() {
panic!("aborted") println!("aborted");
loop {}
} }
#[no_mangle] #[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[lang = "panic_fmt"] #[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! { pub fn oom(layout: core::alloc::Layout) -> ! {
println!("panic at {}:{}: {}", file, line, args); panic!("heap view: {}\ncannot allocate layout: {:?}", unsafe { &ALLOC }, layout)
}
if config::read_str("panic_reboot", |r| r == Ok("1")) { #[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
println!("rebooting..."); #[panic_implementation]
unsafe { board::boot::reboot() } pub fn panic_impl(info: &core::panic::PanicInfo) -> ! {
irq::set_ie(false);
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!("{}", message);
} else {
println!("");
}
println!("backtrace for software version {}:",
include_str!(concat!(env!("OUT_DIR"), "/git-describe")));
let _ = backtrace_artiq::backtrace(|ip| {
// Backtrace gives us the return address, i.e. the address after the delay slot,
// but we're interested in the call instruction.
println!("{:#08x}", ip - 2 * 4);
});
if config::read_str("panic_reset", |r| r == Ok("1")) {
println!("restarting...");
unsafe { boot::reboot() }
} else { } else {
println!("halting."); println!("halting.");
println!("use `artiq_coreconfig write -s panic_reboot 1` to reboot instead"); println!("use `artiq_coreconfig write -s panic_reset 1` to restart instead");
loop {} loop {}
} }
} }
// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort.
// This is never called.
#[allow(non_snake_case)]
#[no_mangle]
pub extern fn _Unwind_Resume() -> ! {
loop {}
}

View File

@ -1,5 +1,6 @@
use log::{self, LevelFilter};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use log::LogLevelFilter;
use logger_artiq::BufferLogger; use logger_artiq::BufferLogger;
use sched::Io; use sched::Io;
use sched::{TcpListener, TcpStream}; use sched::{TcpListener, TcpStream};
@ -27,59 +28,58 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
match Request::read_from(stream)? { match Request::read_from(stream)? {
Request::GetLog => { Request::GetLog => {
BufferLogger::with(|logger| { BufferLogger::with(|logger| {
logger.extract(|log| { let mut buffer = io.until_ok(|| logger.buffer())?;
Reply::LogContent(log).write_to(stream) Reply::LogContent(buffer.extract()).write_to(stream)
})
})?; })?;
}, }
Request::ClearLog => { Request::ClearLog => {
BufferLogger::with(|logger| BufferLogger::with(|logger| -> io::Result<()> {
logger.clear()); let mut buffer = io.until_ok(|| logger.buffer())?;
Ok(buffer.clear())
})?;
Reply::Success.write_to(stream)?; Reply::Success.write_to(stream)?;
}, }
Request::PullLog => { Request::PullLog => {
loop { BufferLogger::with(|logger| -> io::Result<()> {
io.until(|| BufferLogger::with(|logger| !logger.is_empty()))?; loop {
// Do this *before* acquiring the buffer, since that sets the log level
// to OFF.
let log_level = log::max_level();
BufferLogger::with(|logger| { let mut buffer = io.until_ok(|| logger.buffer())?;
let log_level = logger.max_log_level(); if buffer.is_empty() { continue }
logger.extract(|log| {
stream.write_string(log)?;
if log_level == LogLevelFilter::Trace { stream.write_string(buffer.extract())?;
// Hold exclusive access over the logger until we get positive
// acknowledgement; otherwise we get an infinite loop of network
// trace messages being transmitted and causing more network
// trace messages to be emitted.
//
// Any messages unrelated to this management socket that arrive
// while it is flushed are lost, but such is life.
stream.flush()
} else {
Ok(())
}
})?;
Ok(logger.clear()) as io::Result<()> if log_level == LevelFilter::Trace {
})?; // Hold exclusive access over the logger until we get positive
} // acknowledgement; otherwise we get an infinite loop of network
}, // trace messages being transmitted and causing more network
// trace messages to be emitted.
//
// Any messages unrelated to this management socket that arrive
// while it is flushed are lost, but such is life.
stream.flush()?;
}
// Clear the log *after* flushing the network buffers, or we're just
// going to resend all the trace messages on the next iteration.
buffer.clear();
}
})?;
}
Request::SetLogFilter(level) => { Request::SetLogFilter(level) => {
info!("changing log level to {}", level); info!("changing log level to {}", level);
BufferLogger::with(|logger| log::set_max_level(level);
logger.set_max_log_level(level));
Reply::Success.write_to(stream)?; Reply::Success.write_to(stream)?;
}, }
Request::SetUartLogFilter(level) => { Request::SetUartLogFilter(level) => {
info!("changing UART log level to {}", level); info!("changing UART log level to {}", level);
BufferLogger::with(|logger| BufferLogger::with(|logger|
logger.set_uart_log_level(level)); logger.set_uart_log_level(level));
Reply::Success.write_to(stream)?; Reply::Success.write_to(stream)?;
}, }
Request::Hotswap(firmware) => { Request::Hotswap(firmware) => {
Reply::RebootImminent.write_to(stream)?; Reply::RebootImminent.write_to(stream)?;

View File

@ -1,5 +1,4 @@
use config; use board::{csr, config};
use board::csr;
use sched::Io; use sched::Io;
#[cfg(has_rtio_crg)] #[cfg(has_rtio_crg)]
@ -219,16 +218,25 @@ pub fn startup(io: &Io) {
let clk = config::read("startup_clock", |result| { let clk = config::read("startup_clock", |result| {
match result { match result {
Ok(b"i") => RtioClock::Internal, Ok(b"i") => {
Ok(b"e") => RtioClock::External, info!("using internal startup RTIO clock");
_ => { RtioClock::Internal
error!("unrecognized startup_clock configuration entry"); },
Ok(b"e") => {
info!("using external startup RTIO clock");
RtioClock::External
},
Err(()) => {
info!("using internal startup RTIO clock (by default)");
RtioClock::Internal
},
Ok(_) => {
error!("unrecognized startup_clock configuration entry, using internal RTIO clock");
RtioClock::Internal RtioClock::Internal
} }
} }
}); });
info!("startup RTIO clock: {:?}", clk);
if !crg::switch_clock(clk as u8) { if !crg::switch_clock(clk as u8) {
error!("startup RTIO clock failed"); error!("startup RTIO clock failed");
warn!("this may cause the system initialization to fail"); warn!("this may cause the system initialization to fail");
@ -236,13 +244,16 @@ pub fn startup(io: &Io) {
} }
drtio::startup(io); drtio::startup(io);
init_core(); init_core(true);
io.spawn(4096, async_error_thread); io.spawn(4096, async_error_thread);
} }
pub fn init_core() { pub fn init_core(phy: bool) {
unsafe { unsafe {
csr::rtio_core::reset_write(1); csr::rtio_core::reset_write(1);
if phy {
csr::rtio_core::reset_phy_write(1);
}
} }
drtio::init() drtio::init()
} }

View File

@ -20,6 +20,21 @@ SECTIONS
_etext = .; _etext = .;
} > runtime } > runtime
.eh_frame :
{
__eh_frame_start = .;
KEEP(*(.eh_frame))
__eh_frame_end = .;
} > runtime
.eh_frame_hdr :
{
KEEP(*(.eh_frame_hdr))
} > runtime
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
.got : .got :
{ {
@ -79,10 +94,4 @@ SECTIONS
. = ORIGIN(runtime) + LENGTH(runtime); . = ORIGIN(runtime) + LENGTH(runtime);
_eheap = .; _eheap = .;
} > runtime } > runtime
/DISCARD/ :
{
*(.eh_frame)
*(.gcc_except_table)
}
} }

View File

@ -1,22 +1,26 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::mem; use std::mem;
use std::cell::{Cell, RefCell, RefMut}; use std::result;
use std::cell::{Cell, RefCell};
use std::vec::Vec; use std::vec::Vec;
use std::io::{Read, Write, Result, Error, ErrorKind}; use std::io::{Read, Write, Result, Error, ErrorKind};
use fringe::OwnedStack; use fringe::OwnedStack;
use fringe::generator::{Generator, Yielder, State as GeneratorState}; use fringe::generator::{Generator, Yielder, State as GeneratorState};
use smoltcp::time::Duration;
use smoltcp::Error as NetworkError;
use smoltcp::wire::IpEndpoint; use smoltcp::wire::IpEndpoint;
use smoltcp::socket::{AsSocket, SocketHandle}; use smoltcp::socket::{SocketHandle, SocketRef};
type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>;
use board; use board;
use urc::Urc; use urc::Urc;
type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>;
#[derive(Debug)] #[derive(Debug)]
struct WaitRequest { struct WaitRequest {
event: Option<*const (Fn() -> bool + 'static)>, event: Option<*mut FnMut() -> bool>,
timeout: Option<u64> timeout: Option<u64>
} }
@ -123,7 +127,7 @@ impl Scheduler {
pub fn run(&mut self) { pub fn run(&mut self) {
self.sockets.borrow_mut().prune(); self.sockets.borrow_mut().prune();
self.threads.append(&mut *borrow_mut!(self.spawned)); self.threads.append(&mut *self.spawned.borrow_mut());
if self.threads.len() == 0 { return } if self.threads.len() == 0 { return }
let now = board::clock::get_ms(); let now = board::clock::get_ms();
@ -132,26 +136,22 @@ impl Scheduler {
self.run_idx = (self.run_idx + 1) % self.threads.len(); self.run_idx = (self.run_idx + 1) % self.threads.len();
let result = { let result = {
let mut thread = borrow_mut!(self.threads[self.run_idx].0); let &mut Thread { ref mut generator, ref mut interrupted, ref waiting_for } =
match thread.waiting_for { &mut *self.threads[self.run_idx].0.borrow_mut();
_ if thread.interrupted => { if *interrupted {
thread.interrupted = false; *interrupted = false;
thread.generator.resume(WaitResult::Interrupted) generator.resume(WaitResult::Interrupted)
} } else if waiting_for.event.is_none() && waiting_for.timeout.is_none() {
WaitRequest { event: None, timeout: None } => generator.resume(WaitResult::Completed)
thread.generator.resume(WaitResult::Completed), } else if waiting_for.timeout.map(|instant| now >= instant).unwrap_or(false) {
WaitRequest { timeout: Some(instant), .. } if now >= instant => generator.resume(WaitResult::TimedOut)
thread.generator.resume(WaitResult::TimedOut), } else if waiting_for.event.map(|event| unsafe { (*event)() }).unwrap_or(false) {
WaitRequest { event: Some(event), .. } if unsafe { (*event)() } => generator.resume(WaitResult::Completed)
thread.generator.resume(WaitResult::Completed), } else if self.run_idx == start_idx {
_ => { // We've checked every thread and none of them are runnable.
if self.run_idx == start_idx { break
// We've checked every thread and none of them are runnable. } else {
break continue
} else {
continue
}
}
} }
}; };
@ -163,7 +163,7 @@ impl Scheduler {
}, },
Some(wait_request) => { Some(wait_request) => {
// The thread has suspended itself. // The thread has suspended itself.
let mut thread = borrow_mut!(self.threads[self.run_idx].0); let mut thread = self.threads[self.run_idx].0.borrow_mut();
thread.waiting_for = wait_request thread.waiting_for = wait_request
} }
} }
@ -188,7 +188,7 @@ impl<'a> Io<'a> {
pub fn spawn<F>(&self, stack_size: usize, f: F) -> ThreadHandle pub fn spawn<F>(&self, stack_size: usize, f: F) -> ThreadHandle
where F: 'static + FnOnce(Io) + Send { where F: 'static + FnOnce(Io) + Send {
let handle = unsafe { Thread::new(self, stack_size, f) }; let handle = unsafe { Thread::new(self, stack_size, f) };
borrow_mut!(self.spawned).push(handle.clone()); self.spawned.borrow_mut().push(handle.clone());
handle handle
} }
@ -224,13 +224,25 @@ impl<'a> Io<'a> {
}) })
} }
pub fn until<F: Fn() -> bool + 'static>(&self, f: F) -> Result<()> { pub fn until<F: FnMut() -> bool>(&self, mut f: F) -> Result<()> {
let f = unsafe { mem::transmute::<&mut FnMut() -> bool, *mut FnMut() -> bool>(&mut f) };
self.suspend(WaitRequest { self.suspend(WaitRequest {
timeout: None, timeout: None,
event: Some(&f as *const _) event: Some(f)
}) })
} }
pub fn until_ok<T, E, F: FnMut() -> result::Result<T, E>>(&self, mut f: F) -> Result<T> {
let mut value = None;
self.until(|| {
if let Ok(result) = f() {
value = Some(result)
}
value.is_some()
})?;
Ok(value.unwrap())
}
pub fn join(&self, handle: ThreadHandle) -> Result<()> { pub fn join(&self, handle: ThreadHandle) -> Result<()> {
self.until(move || handle.terminated()) self.until(move || handle.terminated())
} }
@ -240,87 +252,18 @@ macro_rules! until {
($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({ ($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({
let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle); let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle);
$socket.io.until(move || { $socket.io.until(move || {
let mut sockets = borrow_mut!(sockets); let mut sockets = sockets.borrow_mut();
let $var: &mut $ty = sockets.get_mut(handle).as_socket(); let $var = sockets.get::<$ty>(handle);
$cond $cond
}) })
}) })
} }
use ::smoltcp::Error as ErrorLower; use ::smoltcp::Error as ErrorLower;
// https://github.com/rust-lang/rust/issues/44057 // https://github.com/rust-lang/rust/issues/26264
// type ErrorLower = ::smoltcp::Error; // type ErrorLower = ::smoltcp::Error;
type UdpPacketBuffer = ::smoltcp::socket::UdpPacketBuffer<'static>;
type UdpSocketBuffer = ::smoltcp::socket::UdpSocketBuffer<'static, 'static>;
type UdpSocketLower = ::smoltcp::socket::UdpSocket<'static, 'static>;
pub struct UdpSocket<'a> {
io: &'a Io<'a>,
handle: SocketHandle
}
impl<'a> UdpSocket<'a> {
pub fn new(io: &'a Io<'a>, buffer_depth: usize, buffer_width: usize) -> UdpSocket<'a> {
let mut rx_buffer = vec![];
let mut tx_buffer = vec![];
for _ in 0..buffer_depth {
rx_buffer.push(UdpPacketBuffer::new(vec![0; buffer_width]));
tx_buffer.push(UdpPacketBuffer::new(vec![0; buffer_width]));
}
let handle = borrow_mut!(io.sockets)
.add(UdpSocketLower::new(
UdpSocketBuffer::new(rx_buffer),
UdpSocketBuffer::new(tx_buffer)));
UdpSocket {
io: io,
handle: handle
}
}
fn as_lower<'b>(&'b self) -> RefMut<'b, UdpSocketLower> {
RefMut::map(borrow_mut!(self.io.sockets),
|sockets| sockets.get_mut(self.handle).as_socket())
}
pub fn bind<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<()> {
match self.as_lower().bind(endpoint) {
Ok(()) => Ok(()),
Err(ErrorLower::Illegal) =>
Err(Error::new(ErrorKind::Other, "already listening")),
Err(ErrorLower::Unaddressable) =>
Err(Error::new(ErrorKind::AddrNotAvailable, "port cannot be zero")),
_ => unreachable!()
}
}
pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint)> {
until!(self, UdpSocketLower, |s| s.can_recv())?;
match self.as_lower().recv_slice(buf) {
Ok(result) => Ok(result),
Err(_) => unreachable!()
}
}
pub fn send_to(&self, buf: &[u8], addr: IpEndpoint) -> Result<()> {
until!(self, UdpSocketLower, |s| s.can_send())?;
match self.as_lower().send_slice(buf, addr) {
Ok(()) => Ok(()),
Err(ErrorLower::Unaddressable) =>
Err(Error::new(ErrorKind::AddrNotAvailable, "unaddressable destination")),
Err(ErrorLower::Truncated) =>
Err(Error::new(ErrorKind::Other, "packet does not fit in buffer")),
Err(_) => unreachable!()
}
}
}
impl<'a> Drop for UdpSocket<'a> {
fn drop(&mut self) {
borrow_mut!(self.io.sockets).release(self.handle)
}
}
type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>; type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>;
type TcpSocketLower = ::smoltcp::socket::TcpSocket<'static>; type TcpSocketLower = ::smoltcp::socket::TcpSocket<'static>;
@ -337,7 +280,8 @@ impl<'a> TcpListener<'a> {
fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle { fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle {
let rx_buffer = vec![0; buffer_size]; let rx_buffer = vec![0; buffer_size];
let tx_buffer = vec![0; buffer_size]; let tx_buffer = vec![0; buffer_size];
borrow_mut!(io.sockets) io.sockets
.borrow_mut()
.add(TcpSocketLower::new( .add(TcpSocketLower::new(
TcpSocketBuffer::new(rx_buffer), TcpSocketBuffer::new(rx_buffer),
TcpSocketBuffer::new(tx_buffer))) TcpSocketBuffer::new(tx_buffer)))
@ -352,35 +296,41 @@ impl<'a> TcpListener<'a> {
} }
} }
fn as_lower<'b>(&'b self) -> RefMut<'b, TcpSocketLower> { fn with_lower<F, R>(&self, f: F) -> R
RefMut::map(borrow_mut!(self.io.sockets), where F: FnOnce(SocketRef<TcpSocketLower>) -> R {
|sockets| sockets.get_mut(self.handle.get()).as_socket()) let mut sockets = self.io.sockets.borrow_mut();
let result = f(sockets.get(self.handle.get()));
result
} }
pub fn is_open(&self) -> bool { pub fn is_open(&self) -> bool {
self.as_lower().is_open() self.with_lower(|s| s.is_open())
} }
pub fn can_accept(&self) -> bool { pub fn can_accept(&self) -> bool {
self.as_lower().is_active() self.with_lower(|s| s.is_active())
} }
pub fn local_endpoint(&self) -> IpEndpoint { pub fn local_endpoint(&self) -> IpEndpoint {
self.as_lower().local_endpoint() self.with_lower(|s| s.local_endpoint())
} }
pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<()> { pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<()> {
let endpoint = endpoint.into(); let endpoint = endpoint.into();
match self.as_lower().listen(endpoint) { self.with_lower(|mut s| s.listen(endpoint))
Ok(()) => Ok(()), .map(|()| {
Err(ErrorLower::Illegal) => self.endpoint.set(endpoint);
Err(Error::new(ErrorKind::Other, "already listening")), ()
Err(ErrorLower::Unaddressable) => })
Err(Error::new(ErrorKind::InvalidInput, "port cannot be zero")), .map_err(|err| {
_ => unreachable!() match err {
}?; ErrorLower::Illegal =>
self.endpoint.set(endpoint); Error::new(ErrorKind::Other, "already listening"),
Ok(()) ErrorLower::Unaddressable =>
Error::new(ErrorKind::InvalidInput, "port cannot be zero"),
_ => unreachable!()
}
})
} }
pub fn accept(&self) -> Result<TcpStream<'a>> { pub fn accept(&self) -> Result<TcpStream<'a>> {
@ -389,8 +339,8 @@ impl<'a> TcpListener<'a> {
// that still counts as accepting even though nothing may be sent. // that still counts as accepting even though nothing may be sent.
let (sockets, handle) = (self.io.sockets.clone(), self.handle.get()); let (sockets, handle) = (self.io.sockets.clone(), self.handle.get());
self.io.until(move || { self.io.until(move || {
let mut sockets = borrow_mut!(sockets); let mut sockets = sockets.borrow_mut();
let socket: &mut TcpSocketLower = sockets.get_mut(handle).as_socket(); let socket = sockets.get::<TcpSocketLower>(handle);
socket.may_send() || socket.may_recv() socket.may_send() || socket.may_recv()
})?; })?;
@ -407,14 +357,14 @@ impl<'a> TcpListener<'a> {
} }
pub fn close(&self) { pub fn close(&self) {
self.as_lower().close() self.with_lower(|mut s| s.close())
} }
} }
impl<'a> Drop for TcpListener<'a> { impl<'a> Drop for TcpListener<'a> {
fn drop(&mut self) { fn drop(&mut self) {
self.as_lower().close(); self.with_lower(|mut s| s.close());
borrow_mut!(self.io.sockets).release(self.handle.get()) self.io.sockets.borrow_mut().release(self.handle.get())
} }
} }
@ -437,57 +387,59 @@ impl<'a> TcpStream<'a> {
} }
} }
fn as_lower<'b>(&'b self) -> RefMut<'b, TcpSocketLower> { fn with_lower<F, R>(&self, f: F) -> R
RefMut::map(borrow_mut!(self.io.sockets), where F: FnOnce(SocketRef<TcpSocketLower>) -> R {
|sockets| sockets.get_mut(self.handle).as_socket()) let mut sockets = self.io.sockets.borrow_mut();
let result = f(sockets.get(self.handle));
result
} }
pub fn is_open(&self) -> bool { pub fn is_open(&self) -> bool {
self.as_lower().is_open() self.with_lower(|s| s.is_open())
} }
pub fn may_send(&self) -> bool { pub fn may_send(&self) -> bool {
self.as_lower().may_send() self.with_lower(|s| s.may_send())
} }
pub fn may_recv(&self) -> bool { pub fn may_recv(&self) -> bool {
self.as_lower().may_recv() self.with_lower(|s| s.may_recv())
} }
pub fn can_send(&self) -> bool { pub fn can_send(&self) -> bool {
self.as_lower().can_send() self.with_lower(|s| s.can_send())
} }
pub fn can_recv(&self) -> bool { pub fn can_recv(&self) -> bool {
self.as_lower().can_recv() self.with_lower(|s| s.can_recv())
} }
pub fn local_endpoint(&self) -> IpEndpoint { pub fn local_endpoint(&self) -> IpEndpoint {
self.as_lower().local_endpoint() self.with_lower(|s| s.local_endpoint())
} }
pub fn remote_endpoint(&self) -> IpEndpoint { pub fn remote_endpoint(&self) -> IpEndpoint {
self.as_lower().remote_endpoint() self.with_lower(|s| s.remote_endpoint())
} }
pub fn timeout(&self) -> Option<u64> { pub fn timeout(&self) -> Option<u64> {
self.as_lower().timeout() self.with_lower(|s| s.timeout().as_ref().map(Duration::millis))
} }
pub fn set_timeout(&self, value: Option<u64>) { pub fn set_timeout(&self, value: Option<u64>) {
self.as_lower().set_timeout(value) self.with_lower(|mut s| s.set_timeout(value.map(Duration::from_millis)))
} }
pub fn keep_alive(&self) -> Option<u64> { pub fn keep_alive(&self) -> Option<u64> {
self.as_lower().keep_alive() self.with_lower(|s| s.keep_alive().as_ref().map(Duration::millis))
} }
pub fn set_keep_alive(&self, value: Option<u64>) { pub fn set_keep_alive(&self, value: Option<u64>) {
self.as_lower().set_keep_alive(value) self.with_lower(|mut s| s.set_keep_alive(value.map(Duration::from_millis)))
} }
pub fn close(&self) -> Result<()> { pub fn close(&self) -> Result<()> {
self.as_lower().close(); self.with_lower(|mut s| s.close());
until!(self, TcpSocketLower, |s| !s.is_open())?; until!(self, TcpSocketLower, |s| !s.is_open())?;
// right now the socket may be in TIME-WAIT state. if we don't give it a chance to send // right now the socket may be in TIME-WAIT state. if we don't give it a chance to send
// a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); } // a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); }
@ -499,12 +451,12 @@ impl<'a> TcpStream<'a> {
impl<'a> Read for TcpStream<'a> { impl<'a> Read for TcpStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
// Only borrow the underlying socket for the span of the next statement. // Only borrow the underlying socket for the span of the next statement.
let result = self.as_lower().recv_slice(buf); let result = self.with_lower(|mut s| s.recv_slice(buf));
match result { match result {
// Slow path: we need to block until buffer is non-empty. // Slow path: we need to block until buffer is non-empty.
Ok(0) => { Ok(0) => {
until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?; until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?;
match self.as_lower().recv_slice(buf) { match self.with_lower(|mut s| s.recv_slice(buf)) {
Ok(length) => Ok(length), Ok(length) => Ok(length),
Err(ErrorLower::Illegal) => Ok(0), Err(ErrorLower::Illegal) => Ok(0),
_ => unreachable!() _ => unreachable!()
@ -523,12 +475,12 @@ impl<'a> Read for TcpStream<'a> {
impl<'a> Write for TcpStream<'a> { impl<'a> Write for TcpStream<'a> {
fn write(&mut self, buf: &[u8]) -> Result<usize> { fn write(&mut self, buf: &[u8]) -> Result<usize> {
// Only borrow the underlying socket for the span of the next statement. // Only borrow the underlying socket for the span of the next statement.
let result = self.as_lower().send_slice(buf); let result = self.with_lower(|mut s| s.send_slice(buf));
match result { match result {
// Slow path: we need to block until buffer is non-full. // Slow path: we need to block until buffer is non-full.
Ok(0) => { Ok(0) => {
until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?; until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?;
match self.as_lower().send_slice(buf) { match self.with_lower(|mut s| s.send_slice(buf)) {
Ok(length) => Ok(length), Ok(length) => Ok(length),
Err(ErrorLower::Illegal) => Ok(0), Err(ErrorLower::Illegal) => Ok(0),
_ => unreachable!() _ => unreachable!()
@ -545,7 +497,7 @@ impl<'a> Write for TcpStream<'a> {
fn flush(&mut self) -> Result<()> { fn flush(&mut self) -> Result<()> {
until!(self, TcpSocketLower, |s| s.send_queue() == 0 || !s.may_send())?; until!(self, TcpSocketLower, |s| s.send_queue() == 0 || !s.may_send())?;
if self.as_lower().send_queue() == 0 { if self.with_lower(|s| s.send_queue()) == 0 {
Ok(()) Ok(())
} else { } else {
Err(Error::new(ErrorKind::ConnectionAborted, "connection aborted")) Err(Error::new(ErrorKind::ConnectionAborted, "connection aborted"))
@ -555,7 +507,7 @@ impl<'a> Write for TcpStream<'a> {
impl<'a> Drop for TcpStream<'a> { impl<'a> Drop for TcpStream<'a> {
fn drop(&mut self) { fn drop(&mut self) {
self.as_lower().close(); self.with_lower(|mut s| s.close());
borrow_mut!(self.io.sockets).release(self.handle) self.io.sockets.borrow_mut().release(self.handle)
} }
} }

View File

@ -3,14 +3,14 @@ use std::{mem, str};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::error::Error; use std::error::Error;
use {config, rtio_mgt, mailbox, rpc_queue, kernel}; use {rtio_mgt, mailbox, rpc_queue, kernel};
use cache::Cache; use cache::Cache;
use rtio_dma::Manager as DmaManager; use rtio_dma::Manager as DmaManager;
use urc::Urc; use urc::Urc;
use sched::{ThreadHandle, Io}; use sched::{ThreadHandle, Io};
use sched::{TcpListener, TcpStream}; use sched::{TcpListener, TcpStream};
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use board; use board::{self, config};
use rpc_proto as rpc; use rpc_proto as rpc;
use session_proto as host; use session_proto as host;
@ -440,7 +440,13 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
kern_acknowledge() kern_acknowledge()
} }
} }
} },
&kern::RpcFlush => {
// See ksupport/lib.rs for the reason this request exists.
// We do not need to do anything here because of how the main loop is
// structured.
kern_acknowledge()
},
&kern::CacheGetRequest { key } => { &kern::CacheGetRequest { key } => {
let value = session.congress.cache.get(key); let value = session.congress.cache.get(key);
@ -508,7 +514,7 @@ fn process_kern_queued_rpc(stream: &mut TcpStream,
let length = NetworkEndian::read_u32(slice) as usize; let length = NetworkEndian::read_u32(slice) as usize;
host_write(stream, host::Reply::RpcRequest { async: true })?; host_write(stream, host::Reply::RpcRequest { async: true })?;
debug!("{:?}", &slice[4..][..length]); debug!("{:?}", &slice[4..][..length]);
stream.write(&slice[4..][..length])?; stream.write_all(&slice[4..][..length])?;
Ok(()) Ok(())
}) })
} }
@ -519,16 +525,16 @@ fn host_kernel_worker(io: &Io,
let mut session = Session::new(congress); let mut session = Session::new(congress);
loop { loop {
while !rpc_queue::empty() {
process_kern_queued_rpc(stream, &mut session)?
}
if stream.can_recv() { if stream.can_recv() {
process_host_message(io, stream, &mut session)? process_host_message(io, stream, &mut session)?
} else if !stream.may_recv() { } else if !stream.may_recv() {
return Ok(()) return Ok(())
} }
while !rpc_queue::empty() {
process_kern_queued_rpc(stream, &mut session)?
}
if mailbox::receive() != 0 { if mailbox::receive() != 0 {
process_kern_message(io, Some(stream), &mut session)?; process_kern_message(io, Some(stream), &mut session)?;
} }
@ -615,7 +621,7 @@ pub fn thread(io: Io) {
{ {
let congress = congress.clone(); let congress = congress.clone();
respawn(&io, &mut kernel_thread, move |io| { respawn(&io, &mut kernel_thread, move |io| {
let mut congress = borrow_mut!(congress); let mut congress = congress.borrow_mut();
info!("running startup kernel"); info!("running startup kernel");
match flash_kernel_worker(&io, &mut congress, "startup_kernel") { match flash_kernel_worker(&io, &mut congress, "startup_kernel") {
Ok(()) => info!("startup kernel finished"), Ok(()) => info!("startup kernel finished"),
@ -650,7 +656,7 @@ pub fn thread(io: Io) {
let congress = congress.clone(); let congress = congress.clone();
let stream = stream.into_handle(); let stream = stream.into_handle();
respawn(&io, &mut kernel_thread, move |io| { respawn(&io, &mut kernel_thread, move |io| {
let mut congress = borrow_mut!(congress); let mut congress = congress.borrow_mut();
let mut stream = TcpStream::from_handle(&io, stream); let mut stream = TcpStream::from_handle(&io, stream);
match host_kernel_worker(&io, &mut stream, &mut *congress) { match host_kernel_worker(&io, &mut stream, &mut *congress) {
Ok(()) => (), Ok(()) => (),
@ -673,7 +679,7 @@ pub fn thread(io: Io) {
let congress = congress.clone(); let congress = congress.clone();
respawn(&io, &mut kernel_thread, move |io| { respawn(&io, &mut kernel_thread, move |io| {
let mut congress = borrow_mut!(congress); let mut congress = congress.borrow_mut();
match flash_kernel_worker(&io, &mut *congress, "idle_kernel") { match flash_kernel_worker(&io, &mut *congress, "idle_kernel") {
Ok(()) => Ok(()) =>
info!("idle kernel finished, standing by"), info!("idle kernel finished, standing by"),

View File

@ -5,25 +5,17 @@ LDFLAGS += -L../libbase
RUSTFLAGS += -Cpanic=abort RUSTFLAGS += -Cpanic=abort
all: satman.bin satman.fbi all:: satman.bin satman.fbi
.PHONY: $(RUSTOUT)/libsatman.a .PHONY: $(RUSTOUT)/libsatman.a
$(RUSTOUT)/libsatman.a: $(RUSTOUT)/libsatman.a:
$(cargo) --manifest-path $(SATMAN_DIRECTORY)/Cargo.toml $(cargo) --manifest-path $(SATMAN_DIRECTORY)/Cargo.toml
satman.elf: $(RUSTOUT)/libsatman.a satman.elf: $(RUSTOUT)/libsatman.a
$(LD) $(LDFLAGS) -T $(SATMAN_DIRECTORY)/satman.ld -o $@ $^ $(link) -T $(SATMAN_DIRECTORY)/satman.ld
@chmod -x $@
%.bin: %.elf %.bin: %.elf
$(OBJCOPY) -O binary $< $@ $(objcopy) -O binary
@chmod -x $@
%.fbi: %.bin %.fbi: %.bin
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< $(mscimg) -f
clean:
$(RM) satman.elf satman.bin satman.fbi
$(RM) -rf cargo
.PHONY: all clean

View File

@ -1,4 +1,4 @@
#![feature(compiler_builtins_lib, lang_items)] #![feature(never_type, panic_implementation, panic_info_message)]
#![no_std] #![no_std]
extern crate compiler_builtins; extern crate compiler_builtins;
@ -241,10 +241,19 @@ pub extern fn abort() {
panic!("aborted") panic!("aborted")
} }
#[no_mangle] #[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[lang = "panic_fmt"] #[panic_implementation]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! { pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
println!("panic at {}:{}: {}", file, line, args); if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {} loop {}
} }

View File

@ -0,0 +1,345 @@
#![feature(never_type, panic_implementation, panic_info_message)]
#![no_std]
#[macro_use]
extern crate log;
#[macro_use]
extern crate board_misoc;
extern crate board_artiq;
use board_misoc::{csr, ident, clock, uart_logger};
use board_artiq::{i2c, spi, si5324, drtioaux};
#[cfg(has_serwb_phy_amc)]
use board_artiq::serwb;
#[cfg(has_hmc830_7043)]
use board_artiq::hmc830_7043;
fn drtio_reset(reset: bool) {
unsafe {
(csr::DRTIO[0].reset_write)(if reset { 1 } else { 0 });
}
}
fn drtio_reset_phy(reset: bool) {
unsafe {
(csr::DRTIO[0].reset_phy_write)(if reset { 1 } else { 0 });
}
}
fn drtio_tsc_loaded() -> bool {
unsafe {
let tsc_loaded = (csr::DRTIO[0].tsc_loaded_read)() == 1;
if tsc_loaded {
(csr::DRTIO[0].tsc_loaded_write)(1);
}
tsc_loaded
}
}
fn process_aux_packet(packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
// and u16 otherwise; hence the `as _` conversion.
match packet {
drtioaux::Packet::EchoRequest =>
drtioaux::send_link(0, &drtioaux::Packet::EchoReply),
drtioaux::Packet::ResetRequest { phy } => {
if phy {
drtio_reset_phy(true);
drtio_reset_phy(false);
} else {
drtio_reset(true);
drtio_reset(false);
}
drtioaux::send_link(0, &drtioaux::Packet::ResetAck)
},
drtioaux::Packet::RtioErrorRequest => {
let errors;
unsafe {
errors = (csr::DRTIO[0].rtio_error_read)();
}
if errors & 1 != 0 {
let channel;
unsafe {
channel = (csr::DRTIO[0].sequence_error_channel_read)();
(csr::DRTIO[0].rtio_error_write)(1);
}
drtioaux::send_link(0,
&drtioaux::Packet::RtioErrorSequenceErrorReply { channel })
} else if errors & 2 != 0 {
let channel;
unsafe {
channel = (csr::DRTIO[0].collision_channel_read)();
(csr::DRTIO[0].rtio_error_write)(2);
}
drtioaux::send_link(0,
&drtioaux::Packet::RtioErrorCollisionReply { channel })
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = (csr::DRTIO[0].busy_channel_read)();
(csr::DRTIO[0].rtio_error_write)(4);
}
drtioaux::send_link(0,
&drtioaux::Packet::RtioErrorBusyReply { channel })
}
else {
drtioaux::send_link(0, &drtioaux::Packet::RtioNoErrorReply)
}
}
drtioaux::Packet::MonitorRequest { channel, probe } => {
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe);
csr::rtio_moninj::mon_value_update_write(1);
value = csr::rtio_moninj::mon_value_read();
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
let reply = drtioaux::Packet::MonitorReply { value: value as u32 };
drtioaux::send_link(0, &reply)
},
drtioaux::Packet::InjectionRequest { channel, overrd, value } => {
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
csr::rtio_moninj::inj_value_write(value);
}
Ok(())
},
drtioaux::Packet::InjectionStatusRequest { channel, overrd } => {
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
value = csr::rtio_moninj::inj_value_read();
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
drtioaux::send_link(0, &drtioaux::Packet::InjectionStatusReply { value: value })
},
drtioaux::Packet::I2cStartRequest { busno } => {
let succeeded = i2c::start(busno).is_ok();
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cRestartRequest { busno } => {
let succeeded = i2c::restart(busno).is_ok();
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cStopRequest { busno } => {
let succeeded = i2c::stop(busno).is_ok();
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cWriteRequest { busno, data } => {
match i2c::write(busno, data) {
Ok(ack) => drtioaux::send_link(0,
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
Err(_) => drtioaux::send_link(0,
&drtioaux::Packet::I2cWriteReply { succeeded: false, ack: false })
}
}
drtioaux::Packet::I2cReadRequest { busno, ack } => {
match i2c::read(busno, ack) {
Ok(data) => drtioaux::send_link(0,
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
Err(_) => drtioaux::send_link(0,
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
}
}
drtioaux::Packet::SpiSetConfigRequest { busno, flags, length, div, cs } => {
let succeeded = spi::set_config(busno, flags, length, div, cs).is_ok();
drtioaux::send_link(0,
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
},
drtioaux::Packet::SpiWriteRequest { busno, data } => {
let succeeded = spi::write(busno, data).is_ok();
drtioaux::send_link(0,
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
}
drtioaux::Packet::SpiReadRequest { busno } => {
match spi::read(busno) {
Ok(data) => drtioaux::send_link(0,
&drtioaux::Packet::SpiReadReply { succeeded: true, data: data }),
Err(_) => drtioaux::send_link(0,
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
}
}
_ => {
warn!("received unexpected aux packet");
Ok(())
}
}
}
fn process_aux_packets() {
let result =
drtioaux::recv_link(0).and_then(|packet| {
if let Some(packet) = packet {
process_aux_packet(packet)
} else {
Ok(())
}
});
match result {
Ok(()) => (),
Err(e) => warn!("aux packet error ({})", e)
}
}
fn process_errors() {
let errors;
unsafe {
errors = (csr::DRTIO[0].protocol_error_read)();
}
if errors & 1 != 0 {
error!("received packet of an unknown type");
}
if errors & 2 != 0 {
error!("received truncated packet");
}
if errors & 4 != 0 {
let channel;
let timestamp_event;
let timestamp_counter;
unsafe {
channel = (csr::DRTIO[0].underflow_channel_read)();
timestamp_event = (csr::DRTIO[0].underflow_timestamp_event_read)() as i64;
timestamp_counter = (csr::DRTIO[0].underflow_timestamp_counter_read)() as i64;
}
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
}
if errors & 8 != 0 {
error!("write overflow");
}
unsafe {
(csr::DRTIO[0].protocol_error_write)(errors);
}
}
#[cfg(rtio_frequency = "150.0")]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 6,
nc1_ls : 6,
n2_hs : 10,
n2_ls : 270,
n31 : 75,
n32 : 75,
bwsel : 4,
crystal_ref: true
};
fn drtio_link_rx_up() -> bool {
unsafe {
(csr::DRTIO[0].rx_up_read)() == 1
}
}
const SIPHASER_PHASE: u16 = 32;
#[no_mangle]
pub extern fn main() -> i32 {
clock::init();
uart_logger::ConsoleLogger::register();
info!("ARTIQ satellite manager starting...");
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
info!("gateware ident {}", ident::read(&mut [0; 64]));
#[cfg(has_slave_fpga_cfg)]
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
#[cfg(has_serwb_phy_amc)]
serwb::wait_init();
i2c::init();
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(has_hmc830_7043)]
/* must be the first SPI init because of HMC830 SPI mode selection */
hmc830_7043::init().expect("cannot initialize HMC830/7043");
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
#[cfg(has_ad9154)]
{
board_artiq::ad9154::jesd_unreset();
board_artiq::ad9154::init();
}
#[cfg(has_allaki_atts)]
board_artiq::hmc542::program_all(8/*=4dB*/);
loop {
while !drtio_link_rx_up() {
process_errors();
}
info!("link is up, switching to recovered clock");
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew(SIPHASER_PHASE).expect("failed to calibrate skew");
drtioaux::reset(0);
drtio_reset(false);
drtio_reset_phy(false);
while drtio_link_rx_up() {
process_errors();
process_aux_packets();
if drtio_tsc_loaded() {
#[cfg(has_ad9154)]
{
if let Err(e) = board_artiq::jesd204sync::sysref_auto_rtio_align() {
error!("failed to align SYSREF at FPGA: {}", e);
}
if let Err(e) = board_artiq::jesd204sync::sysref_auto_dac_align() {
error!("failed to align SYSREF at DAC: {}", e);
}
}
if let Err(e) = drtioaux::send_link(0, &drtioaux::Packet::TSCAck) {
error!("aux packet error: {}", e);
}
}
}
drtio_reset_phy(true);
drtio_reset(true);
drtio_tsc_loaded();
info!("link is down, switching to local crystal clock");
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
}
}
#[no_mangle]
pub extern fn exception(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
}
#[no_mangle]
pub extern fn abort() {
println!("aborted");
loop {}
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}

View File

@ -53,6 +53,8 @@ def main():
dev = Tdc(args.device) dev = Tdc(args.device)
elif product == "tpz001": elif product == "tpz001":
dev = Tpz(args.device) dev = Tpz(args.device)
loop = asyncio.get_event_loop()
loop.run_until_complete(dev.get_tpz_io_settings())
else: else:
print("Invalid product string (-P/--product), " print("Invalid product string (-P/--product), "
"choose from tdc001 or tpz001") "choose from tdc001 or tpz001")

View File

@ -51,6 +51,10 @@ Prerequisites:
help="target adapter, default: %(default)s") help="target adapter, default: %(default)s")
parser.add_argument("--target-file", default=None, parser.add_argument("--target-file", default=None,
help="use alternative OpenOCD target file") help="use alternative OpenOCD target file")
parser.add_argument("-I", "--preinit-command", default=[], action="append",
help="add a pre-initialization OpenOCD command. "
"Useful for selecting a development board "
"when several are connected.")
parser.add_argument("-f", "--storage", help="write file to storage area") parser.add_argument("-f", "--storage", help="write file to storage area")
parser.add_argument("-d", "--dir", help="look for files in this directory") parser.add_argument("-d", "--dir", help="look for files in this directory")
parser.add_argument("action", metavar="ACTION", nargs="*", parser.add_argument("action", metavar="ACTION", nargs="*",
@ -69,8 +73,8 @@ def main():
"start": "xc7_program xc7.tap", "start": "xc7_program xc7.tap",
"gateware": 0x000000, "gateware": 0x000000,
"bios": 0xaf0000, "bios": 0xaf0000,
"runtime": 0xb00000, "storage": 0xb30000,
"storage": 0xb80000, "runtime": 0xb40000,
}, },
}[opts.target] }[opts.target]
@ -84,6 +88,7 @@ def main():
conv = False conv = False
prog = [] prog = []
prog.extend(opts.preinit_command)
prog.append("init") prog.append("init")
for action in opts.action: for action in opts.action:
if action == "proxy": if action == "proxy":

View File

@ -94,26 +94,26 @@ class DBWriter(TaskObject):
"too many pending updates", k) "too many pending updates", k)
async def _do(self): async def _do(self):
while True: async with aiohttp.ClientSession() as session:
k, v, t = await self._queue.get() while True:
url = self.base_url + "/write" k, v, t = await self._queue.get()
params = {"u": self.user, "p": self.password, "db": self.database, url = self.base_url + "/write"
"precision": "ms"} params = {"u": self.user, "p": self.password, "db": self.database,
data = "{},dataset={} {} {}".format( "precision": "ms"}
self.table, k, format_influxdb(v), round(t*1e3)) data = "{},dataset={} {} {}".format(
try: self.table, k, format_influxdb(v), round(t*1e3))
response = await aiohttp.request( try:
"POST", url, params=params, data=data) response = await session.post(url, params=params, data=data)
except: except:
logger.warning("got exception trying to update '%s'", logger.warning("got exception trying to update '%s'",
k, exc_info=True) k, exc_info=True)
else: else:
if response.status not in (200, 204): if response.status not in (200, 204):
content = (await response.content.read()).decode().strip() content = (await response.content.read()).decode().strip()
logger.warning("got HTTP status %d " logger.warning("got HTTP status %d "
"trying to update '%s': %s", "trying to update '%s': %s",
response.status, k, content) response.status, k, content)
response.close() response.close()
class _Mock: class _Mock:

View File

@ -4,8 +4,8 @@
# * tcpdump has CAP_NET_RAW capabilities set # * tcpdump has CAP_NET_RAW capabilities set
# use # setcap cap_net_raw+eip /usr/sbin/tcpdump # use # setcap cap_net_raw+eip /usr/sbin/tcpdump
import argparse
import os import os
import argparse
import subprocess import subprocess
from artiq.tools import verbosity_args, init_logger, logger, SSHClient from artiq.tools import verbosity_args, init_logger, logger, SSHClient
@ -49,7 +49,6 @@ def main():
subprocess.check_call(args.command) subprocess.check_call(args.command)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
logger.error("Command failed") logger.error("Command failed")
sys.exit(1)
tcpdump.close() tcpdump.close()
sftp.get("{tmp}/trace.pcap".format(tmp=client.tmp), sftp.get("{tmp}/trace.pcap".format(tmp=client.tmp),

View File

@ -6,6 +6,7 @@ import sys
import traceback import traceback
import numpy as np # Needed to use numpy in RPC call arguments on cmd line import numpy as np # Needed to use numpy in RPC call arguments on cmd line
import pprint import pprint
import inspect
from artiq.protocols.pc_rpc import AutoTarget, Client from artiq.protocols.pc_rpc import AutoTarget, Client
@ -46,33 +47,7 @@ def list_methods(remote):
print(doc["docstring"]) print(doc["docstring"])
print() print()
for name, (argspec, docstring) in sorted(doc["methods"].items()): for name, (argspec, docstring) in sorted(doc["methods"].items()):
args = "" print(name + inspect.formatargspec(**argspec))
for arg in argspec["args"]:
args += arg
if argspec["defaults"] is not None:
kword_index = len(argspec["defaults"]) - len(argspec["args"])\
+ argspec["args"].index(arg)
if kword_index >= 0:
if argspec["defaults"][kword_index] == Ellipsis:
args += "=..."
else:
args += "={}".format(argspec["defaults"][kword_index])
if argspec["args"].index(arg) < len(argspec["args"]) - 1:
args += ", "
if argspec["varargs"] is not None:
args += ", *{}".format(argspec["varargs"])
elif len(argspec["kwonlyargs"]) > 0:
args += ", *"
for kwonlyarg in argspec["kwonlyargs"]:
args += ", {}".format(kwonlyarg)
if kwonlyarg in argspec["kwonlydefaults"]:
if argspec["kwonlydefaults"][kwonlyarg] == Ellipsis:
args += "=..."
else:
args += "={}".format(argspec["kwonlydefaults"][kwonlyarg])
if argspec["varkw"] is not None:
args += ", **{}".format(argspec["varkw"])
print("{}({})".format(name, args))
if docstring is not None: if docstring is not None:
print(textwrap.indent(docstring, " ")) print(textwrap.indent(docstring, " "))
print() print()

View File

@ -17,11 +17,12 @@ class _OSERDESE2_8X(Module):
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
o_OQ=pad_o, o_TQ=self.t_out, o_OQ=pad_o, o_TQ=self.t_out,
i_RST=ResetSignal("rio_phy"),
i_CLK=ClockSignal("rtiox4"), i_CLK=ClockSignal("rtiox4"),
i_CLKDIV=ClockSignal("rio_phy"), i_CLKDIV=ClockSignal("rio_phy"),
i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3], i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3],
i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7], i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7],
i_TCE=1, i_OCE=1, i_RST=0, i_TCE=1, i_OCE=1,
i_T1=self.t_in) i_T1=self.t_in)
if pad_n is None: if pad_n is None:
self.comb += pad.eq(pad_o) self.comb += pad.eq(pad_o)
@ -49,7 +50,8 @@ class _ISERDESE2_8X(Module):
i_D=pad_i, i_D=pad_i,
i_CLK=ClockSignal("rtiox4"), i_CLK=ClockSignal("rtiox4"),
i_CLKB=~ClockSignal("rtiox4"), i_CLKB=~ClockSignal("rtiox4"),
i_CE1=1, i_RST=0, i_CE1=1,
i_RST=ResetSignal("rio_phy"),
i_CLKDIV=ClockSignal("rio_phy")) i_CLKDIV=ClockSignal("rio_phy"))
if pad_n is None: if pad_n is None:
self.comb += pad_i.eq(pad) self.comb += pad_i.eq(pad)

View File

@ -115,166 +115,3 @@ class InOut(Module):
self.submodules += pe self.submodules += pe
self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width)) self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width))
self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o) self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o)
class _FakeSerdes(Module):
def __init__(self):
self.o = Signal(8)
self.i = Signal(8)
self.oe = Signal()
class _OutputTB(Module):
def __init__(self):
serdes = _FakeSerdes()
self.submodules.dut = RenameClockDomains(Output(serdes),
{"rio_phy": "sys"})
def gen_simulation(self, selfp):
selfp.dut.rtlink.o.data = 1
selfp.dut.rtlink.o.fine_ts = 1
selfp.dut.rtlink.o.stb = 1
yield
selfp.dut.rtlink.o.stb = 0
yield
selfp.dut.rtlink.o.data = 0
selfp.dut.rtlink.o.fine_ts = 2
selfp.dut.rtlink.o.stb = 1
yield
yield
selfp.dut.rtlink.o.data = 1
selfp.dut.rtlink.o.fine_ts = 7
selfp.dut.rtlink.o.stb = 1
for _ in range(6):
# note that stb stays active; output should not change
yield
class _InOutTB(Module):
def __init__(self):
self.serdes = _FakeSerdes()
self.submodules.dut = RenameClockDomains(InOut(self.serdes),
{"rio_phy": "sys",
"rio": "sys"})
def check_input(self, selfp, stb, fine_ts=None):
if stb != selfp.dut.rtlink.i.stb:
print("KO rtlink.i.stb should be {} but is {}"
.format(stb, selfp.dut.rtlink.i.stb))
elif fine_ts is not None and fine_ts != selfp.dut.rtlink.i.fine_ts:
print("KO rtlink.i.fine_ts should be {} but is {}"
.format(fine_ts, selfp.dut.rtlink.i.fine_ts))
else:
print("OK")
def check_output(self, selfp, data):
if selfp.serdes.o != data:
print("KO io.o should be {} but is {}".format(data, selfp.serdes.o))
else:
print("OK")
def check_output_enable(self, selfp, oe):
if selfp.serdes.oe != oe:
print("KO io.oe should be {} but is {}".format(oe, selfp.serdes.oe))
else:
print("OK")
def gen_simulation(self, selfp):
selfp.dut.rtlink.o.address = 2
selfp.dut.rtlink.o.data = 0b11
selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising + falling
yield
selfp.dut.rtlink.o.stb = 0
self.check_output_enable(selfp, 0)
yield
selfp.serdes.i = 0b11111110 # rising edge at fine_ts = 1
yield
selfp.serdes.i = 0b11111111
yield
self.check_input(selfp, stb=1, fine_ts=1)
selfp.serdes.i = 0b01111111 # falling edge at fine_ts = 7
yield
selfp.serdes.i = 0b00000000
yield
self.check_input(selfp, stb=1, fine_ts=7)
selfp.serdes.i = 0b11000000 # rising edge at fine_ts = 6
yield
selfp.serdes.i = 0b11111111
yield
self.check_input(selfp, stb=1, fine_ts=6)
selfp.dut.rtlink.o.address = 2
selfp.dut.rtlink.o.data = 0b11
selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising only
yield
selfp.dut.rtlink.o.stb = 0
yield
selfp.serdes.i = 0b00001111 # falling edge at fine_ts = 4
yield
self.check_input(selfp, stb=0) # no strobe, sensitivity is rising edge
selfp.serdes.i = 0b11110000 # rising edge at fine_ts = 4
yield
self.check_input(selfp, stb=1, fine_ts=4)
selfp.dut.rtlink.o.address = 1
selfp.dut.rtlink.o.data = 1
selfp.dut.rtlink.o.stb = 1 # set Output Enable to 1
yield
selfp.dut.rtlink.o.stb = 0
yield
yield
self.check_output_enable(selfp, 1)
selfp.dut.rtlink.o.address = 0
selfp.dut.rtlink.o.data = 1
selfp.dut.rtlink.o.fine_ts = 3
selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 3
yield
selfp.dut.rtlink.o.stb = 0
yield
self.check_output(selfp, data=0b11111000)
yield
self.check_output(selfp, data=0xFF) # stays at 1
selfp.dut.rtlink.o.data = 0
selfp.dut.rtlink.o.fine_ts = 0
selfp.dut.rtlink.o.stb = 1 # falling edge at fine_ts = 0
yield
selfp.dut.rtlink.o.stb = 0
yield
self.check_output(selfp, data=0)
yield
self.check_output(selfp, data=0)
selfp.dut.rtlink.o.data = 1
selfp.dut.rtlink.o.fine_ts = 7
selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 7
yield
selfp.dut.rtlink.o.stb = 0
yield
self.check_output(selfp, data=0b10000000)
if __name__ == "__main__":
import sys
from migen.sim.generic import Simulator, TopLevel
if len(sys.argv) != 2:
print("Incorrect command line")
sys.exit(1)
cls = {
"output": _OutputTB,
"inout": _InOutTB
}[sys.argv[1]]
with Simulator(cls(), TopLevel("top.vcd", clk_period=int(1/0.125))) as s:
s.run()

View File

@ -208,9 +208,11 @@ class SPIMaster(Module):
clk_t = TSTriple() clk_t = TSTriple()
self.specials += clk_t.get_tristate(pads.clk) self.specials += clk_t.get_tristate(pads.clk)
self.comb += [ self.comb += clk_t.oe.eq(~config.offline),
clk_t.oe.eq(~config.offline), self.sync += [
clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), If(spi.cg.ce & spi.cg.edge,
clk_t.o.eq((~spi.cg.clk & spi.cs_next) ^ config.clk_polarity)
)
] ]
mosi_t = TSTriple() mosi_t = TSTriple()

View File

@ -157,7 +157,6 @@ class Phaser(MiniSoC, AMPSoC):
"rtio": 0x20000000, "rtio": 0x20000000,
"rtio_dma": 0x30000000, "rtio_dma": 0x30000000,
"mailbox": 0x70000000, "mailbox": 0x70000000,
"ad9154": 0x50000000,
} }
mem_map.update(MiniSoC.mem_map) mem_map.update(MiniSoC.mem_map)

View File

@ -127,6 +127,7 @@ class SMA_SPI(_NIST_Ions):
self.submodules.cri_con = rtio.CRIInterconnectShared( self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri], [self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri]) [self.rtio_core.cri])
self.register_kernel_cpu_csrdevice("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")

View File

@ -0,0 +1,125 @@
import unittest
from migen import *
from artiq.gateware.rtio.phy.ttl_serdes_generic import *
class _FakeSerdes:
def __init__(self):
self.o = Signal(8)
self.i = Signal(8)
self.oe = Signal()
class _TB(Module):
def __init__(self):
self.serdes = _FakeSerdes()
self.submodules.dut = ClockDomainsRenamer({"rio_phy": "sys", "rio": "sys"})(
InOut(self.serdes))
class TestTTLSerdes(unittest.TestCase):
def test_input(self):
tb = _TB()
def gen():
yield tb.dut.rtlink.o.address.eq(2)
yield tb.dut.rtlink.o.data.eq(0b11)
yield tb.dut.rtlink.o.stb.eq(1) # set sensitivity to rising + falling
yield
yield tb.dut.rtlink.o.stb.eq(0)
yield
self.assertEqual((yield tb.serdes.oe), 0)
self.assertEqual((yield tb.dut.rtlink.i.stb), 0)
yield tb.serdes.i.eq(0b11111110) # rising edge at fine_ts = 1
yield
yield tb.serdes.i.eq(0b11111111)
yield
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 1)
yield tb.serdes.i.eq(0b01111111) # falling edge at fine_ts = 7
yield
yield tb.serdes.i.eq(0b00000000)
yield
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 7)
yield tb.serdes.i.eq(0b11000000) # rising edge at fine_ts = 6
yield
yield tb.serdes.i.eq(0b11111111)
yield
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 6)
yield tb.dut.rtlink.o.address.eq(2)
yield tb.dut.rtlink.o.data.eq(0b01)
yield tb.dut.rtlink.o.stb.eq(1) # set sensitivity to rising only
yield
yield tb.dut.rtlink.o.stb.eq(0)
yield
yield tb.serdes.i.eq(0b00001111) # falling edge at fine_ts = 4
yield
yield tb.serdes.i.eq(0b00000000)
yield
# no strobe, sensitivity is rising edge
self.assertEqual((yield tb.dut.rtlink.i.stb), 0)
yield tb.serdes.i.eq(0b11110000) # rising edge at fine_ts = 4
yield
yield tb.serdes.i.eq(0b11111111)
yield
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 4)
run_simulation(tb, gen())
def test_output(self):
tb = _TB()
def gen():
yield tb.dut.rtlink.o.address.eq(1)
yield tb.dut.rtlink.o.data.eq(1)
yield tb.dut.rtlink.o.stb.eq(1) # set Output Enable to 1
yield
yield tb.dut.rtlink.o.stb.eq(0)
yield
yield
self.assertEqual((yield tb.serdes.oe), 1)
yield tb.dut.rtlink.o.address.eq(0)
yield tb.dut.rtlink.o.data.eq(1)
yield tb.dut.rtlink.o.fine_ts.eq(3)
yield tb.dut.rtlink.o.stb.eq(1) # rising edge at fine_ts = 3
yield
yield tb.dut.rtlink.o.stb.eq(0)
yield
self.assertEqual((yield tb.serdes.o), 0b11111000)
yield
self.assertEqual((yield tb.serdes.o), 0b11111111) # stays at 1
yield tb.dut.rtlink.o.data.eq(0)
yield tb.dut.rtlink.o.fine_ts.eq(0)
yield tb.dut.rtlink.o.stb.eq(1) # falling edge at fine_ts = 0
yield
yield tb.dut.rtlink.o.stb.eq(0)
yield
self.assertEqual((yield tb.serdes.o), 0b00000000)
yield
self.assertEqual((yield tb.serdes.o), 0b00000000)
yield tb.dut.rtlink.o.data.eq(1)
yield tb.dut.rtlink.o.fine_ts.eq(7)
yield tb.dut.rtlink.o.stb.eq(1) # rising edge at fine_ts = 7
yield
yield tb.dut.rtlink.o.stb.eq(0)
yield
self.assertEqual((yield tb.serdes.o), 0b10000000)
run_simulation(tb, gen())

View File

@ -321,7 +321,6 @@ class AppletsDock(QtWidgets.QDockWidget):
self.main_window = main_window self.main_window = main_window
self.datasets_sub = datasets_sub self.datasets_sub = datasets_sub
self.dock_to_item = dict()
self.applet_uids = set() self.applet_uids = set()
self.table = QtWidgets.QTreeWidget() self.table = QtWidgets.QTreeWidget()
@ -414,12 +413,12 @@ class AppletsDock(QtWidgets.QDockWidget):
finally: finally:
self.table.itemChanged.connect(self.item_changed) self.table.itemChanged.connect(self.item_changed)
def create(self, uid, name, spec): def create(self, item, name, spec):
dock = _AppletDock(self.datasets_sub, uid, name, spec) dock = _AppletDock(self.datasets_sub, item.applet_uid, name, spec)
self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
dock.setFloating(True) dock.setFloating(True)
asyncio.ensure_future(dock.start()) asyncio.ensure_future(dock.start())
dock.sigClosed.connect(partial(self.on_dock_closed, dock)) dock.sigClosed.connect(partial(self.on_dock_closed, item, dock))
return dock return dock
def item_changed(self, item, column): def item_changed(self, item, column):
@ -437,15 +436,15 @@ class AppletsDock(QtWidgets.QDockWidget):
if item.applet_dock is None: if item.applet_dock is None:
name = item.text(0) name = item.text(0)
spec = self.get_spec(item) spec = self.get_spec(item)
dock = self.create(item.applet_uid, name, spec) dock = self.create(item, name, spec)
item.applet_dock = dock item.applet_dock = dock
if item.applet_geometry is not None: if item.applet_geometry is not None:
dock.restoreGeometry(item.applet_geometry) dock.restoreGeometry(item.applet_geometry)
# geometry is now handled by main window state # geometry is now handled by main window state
item.applet_geometry = None item.applet_geometry = None
self.dock_to_item[dock] = item
else: else:
dock = item.applet_dock dock = item.applet_dock
item.applet_dock = None
if dock is not None: if dock is not None:
# This calls self.on_dock_closed # This calls self.on_dock_closed
dock.close() dock.close()
@ -455,12 +454,9 @@ class AppletsDock(QtWidgets.QDockWidget):
else: else:
raise ValueError raise ValueError
def on_dock_closed(self, dock): def on_dock_closed(self, item, dock):
item = self.dock_to_item[dock]
item.applet_dock = None
item.applet_geometry = dock.saveGeometry() item.applet_geometry = dock.saveGeometry()
asyncio.ensure_future(dock.terminate()) asyncio.ensure_future(dock.terminate())
del self.dock_to_item[dock]
item.setCheckState(0, QtCore.Qt.Unchecked) item.setCheckState(0, QtCore.Qt.Unchecked)
def get_untitled(self): def get_untitled(self):

View File

@ -12,17 +12,17 @@
version="1.1" version="1.1"
x="0px" x="0px"
y="0px" y="0px"
width="360.147" width="323.49704"
height="432.04401" height="432.04401"
viewBox="0 0 360.147 432.04401" viewBox="0 0 323.49704 432.04401"
enable-background="new 0 0 800 800" enable-background="new 0 0 800 800"
xml:space="preserve" xml:space="preserve"
id="svg2" id="svg2"
inkscape:version="0.91 r13725" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="logo_ver.svg"><metadata sodipodi:docname="logo_ver.svg"><metadata
id="metadata548"><rdf:RDF><cc:Work id="metadata548"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs546" /><sodipodi:namedview id="defs546" /><sodipodi:namedview
pagecolor="#ffffff" pagecolor="#ffffff"
bordercolor="#666666" bordercolor="#666666"
@ -33,7 +33,7 @@
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1156" inkscape:window-height="1124"
id="namedview544" id="namedview544"
showgrid="false" showgrid="false"
fit-margin-top="0" fit-margin-top="0"
@ -41,12 +41,12 @@
fit-margin-right="0" fit-margin-right="0"
fit-margin-bottom="0" fit-margin-bottom="0"
inkscape:zoom="0.834386" inkscape:zoom="0.834386"
inkscape:cx="-244.98179" inkscape:cx="-466.10247"
inkscape:cy="163.75581" inkscape:cy="163.7558"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="44" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><path inkscape:current-layer="text3371" /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path381" id="path381"
@ -54,7 +54,7 @@
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path383" id="path383"
d="m 53.761,135.723 10.609,9.812 c 0.215,-0.162 0.461,-0.348 0.689,-0.549 20.543,-18.001 43.98,-33.619 69.662,-46.423 32.912,-16.404 60.969,-25.002 88.295,-27.058 2.504,-0.188 4.811,-0.279 7.051,-0.279 9.105,0 16.873,1.591 23.744,4.864 7.635,3.636 11.473,9.741 11.404,18.146 -0.053,6.609 -1.955,13.229 -5.812,20.239 -2.68,4.868 -5.713,9.68 -8.646,14.332 -1.248,1.979 -2.502,3.969 -3.744,5.982 l 10.135,9.65 c 8.092,-10.235 16.82,-22.731 20.846,-38.001 0.467,-1.765 0.861,-3.586 1.244,-5.348 0.174,-0.804 0.348,-1.606 0.529,-2.408 l 0,-8.887 c -0.049,-0.148 -0.102,-0.297 -0.154,-0.444 -0.141,-0.387 -0.285,-0.787 -0.357,-1.216 -2.037,-12.213 -8.967,-20.778 -21.184,-26.186 -7.824,-3.462 -16.289,-4.355 -23.535,-4.772 -2.264,-0.13 -4.576,-0.196 -6.877,-0.196 -11.945,0 -24.328,1.727 -37.859,5.278 -46.736,12.272 -90.896,35.554 -131.252,69.197 -1.098,0.917 -2.182,1.903 -3.332,2.947 -0.465,0.425 -0.948,0.863 -1.456,1.32 z" /><path d="m 53.761,135.723 10.609,9.812 c 0.215,-0.162 0.461,-0.348 0.689,-0.549 20.543,-18.001 43.98,-33.619 69.662,-46.423 32.912,-16.404 60.969,-25.002 88.295,-27.058 2.504,-0.188 4.811,-0.279 7.051,-0.279 9.105,0 16.873,1.591 23.744,4.864 7.635,3.636 11.473,9.741 11.404,18.146 -0.053,6.609 -1.955,13.229 -5.812,20.239 -2.68,4.868 -5.713,9.68 -8.646,14.332 -1.248,1.979 -2.502,3.969 -3.744,5.982 l 10.135,9.65 c 8.092,-10.235 16.82,-22.731 20.846,-38.001 0.467,-1.765 0.861,-3.586 1.244,-5.348 0.174,-0.804 0.348,-1.606 0.529,-2.408 v -8.887 c -0.049,-0.148 -0.102,-0.297 -0.154,-0.444 -0.141,-0.387 -0.285,-0.787 -0.357,-1.216 -2.037,-12.213 -8.967,-20.778 -21.184,-26.186 -7.824,-3.462 -16.289,-4.355 -23.535,-4.772 -2.264,-0.13 -4.576,-0.196 -6.877,-0.196 -11.945,0 -24.328,1.727 -37.859,5.278 -46.736,12.272 -90.896,35.554 -131.252,69.197 -1.098,0.917 -2.182,1.903 -3.332,2.947 -0.465,0.425 -0.948,0.863 -1.456,1.32 z" /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path385" id="path385"
@ -82,11 +82,11 @@
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path397" id="path397"
d="m 180.738,111.632 c -0.625,-4.189 -1.227,-8.218 -1.867,-12.238 -0.326,-2.036 -5.861,-6.224 -8.229,-6.224 -0.156,0 -0.291,0.02 -0.402,0.058 -4.172,1.46 -8.242,3.096 -12.551,4.827 -1.42,0.57 -2.855,1.146 -4.316,1.727 l 28,16.088 -0.635,-4.238 z" /><path d="m 180.738,111.632 c -0.625,-4.189 -1.227,-8.218 -1.867,-12.238 -0.326,-2.036 -5.861,-6.224 -8.229,-6.224 -0.156,0 -0.291,0.02 -0.402,0.058 -4.172,1.46 -8.242,3.096 -12.551,4.827 -1.42,0.57 -2.855,1.146 -4.316,1.727 l 28,16.088 z" /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path399" id="path399"
d="m 90.154,136.545 c -0.338,0.264 -0.668,0.525 -1,0.788 -0.463,0.366 -0.936,0.736 -1.393,1.099 -2.838,2.248 -5.516,4.371 -8.346,6.353 -2.75,1.927 -3.779,4.095 -3.336,7.03 0.102,0.675 0.096,1.436 0.09,2.17 -0.01,1.219 -0.02,2.479 0.488,2.946 3.336,3.059 6.891,5.851 10.654,8.807 0.605,0.477 1.227,0.968 1.842,1.452 0.334,0.264 0.664,0.523 1,0.789 0.188,0.148 0.369,0.29 0.557,0.439 l 0,-32.312 c -0.189,0.148 -0.369,0.291 -0.556,0.439 z" /><path d="m 90.154,136.545 c -0.338,0.264 -0.668,0.525 -1,0.788 -0.463,0.366 -0.936,0.736 -1.393,1.099 -2.838,2.248 -5.516,4.371 -8.346,6.353 -2.75,1.927 -3.779,4.095 -3.336,7.03 0.102,0.675 0.096,1.436 0.09,2.17 -0.01,1.219 -0.02,2.479 0.488,2.946 3.336,3.059 6.891,5.851 10.654,8.807 0.605,0.477 1.227,0.968 1.842,1.452 0.334,0.264 0.664,0.523 1,0.789 0.188,0.148 0.369,0.29 0.557,0.439 v -32.312 c -0.189,0.148 -0.369,0.291 -0.556,0.439 z" /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path401" id="path401"
@ -98,7 +98,7 @@
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path405" id="path405"
d="M 231.324,171.353 221.01,161.63 c -0.9,2.513 -2.059,14.3 -1.457,19.737 l 11.771,-10.014 z" /><path d="M 231.324,171.353 221.01,161.63 c -0.9,2.513 -2.059,14.3 -1.457,19.737 z" /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path407" id="path407"
@ -117,25 +117,25 @@
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path415" id="path415"
d="m 366.13,593.558 c 7.498,-5.083 10.756,-15.119 7.922,-24.407 -2.773,-9.089 -10.521,-14.536 -20.727,-14.573 l -26.715,0 0,61.202 14.156,0 0,-18.602 9.979,0 10.836,18.602 16.479,0 0,-1.06 -12.725,-20.623 0.795,-0.539 z m -12.801,-8.731 -12.562,0 0,-17.809 1.004,0 c 1.254,0 2.529,-0.01 3.811,-0.02 2.6,-0.021 5.225,-0.041 7.771,0.02 5.328,0.052 7.74,4.621 7.719,8.845 -0.022,4.454 -2.688,8.964 -7.743,8.964 z" /><polygon d="m 366.13,593.558 c 7.498,-5.083 10.756,-15.119 7.922,-24.407 -2.773,-9.089 -10.521,-14.536 -20.727,-14.573 H 326.61 v 61.202 h 14.156 v -18.602 h 9.979 l 10.836,18.602 h 16.479 v -1.06 l -12.725,-20.623 z m -12.801,-8.731 h -12.562 v -17.809 h 1.004 c 1.254,0 2.529,-0.01 3.811,-0.02 2.6,-0.021 5.225,-0.041 7.771,0.02 5.328,0.052 7.74,4.621 7.719,8.845 -0.022,4.454 -2.688,8.964 -7.743,8.964 z" /><polygon
style="fill:#ffffff" style="fill:#ffffff"
id="polygon417" id="polygon417"
points="432.151,554.577 387.202,554.577 387.202,567.019 402.733,567.019 402.733,615.779 416.71,615.779 416.71,567.019 432.151,567.019 " /><polygon points="387.202,567.019 402.733,567.019 402.733,615.779 416.71,615.779 416.71,567.019 432.151,567.019 432.151,554.577 387.202,554.577 " /><polygon
style="fill:#ffffff" style="fill:#ffffff"
id="polygon419" id="polygon419"
points="464.571,566.657 471.976,566.657 471.976,554.577 443.19,554.577 443.19,566.657 450.595,566.657 450.595,603.608 442.558,603.608 442.558,615.779 472.606,615.779 472.606,603.608 464.571,603.608 " /><path points="471.976,554.577 443.19,554.577 443.19,566.657 450.595,566.657 450.595,603.608 442.558,603.608 442.558,615.779 472.606,615.779 472.606,603.608 464.571,603.608 464.571,566.657 471.976,566.657 " /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path421" id="path421"
d="m 542.927,603.421 c 3.252,-5.109 4.9,-11.218 4.9,-18.153 0,-21.831 -16.283,-31.774 -31.414,-31.774 -15.129,0 -31.414,9.943 -31.414,31.774 0,21.683 16.174,31.56 31.199,31.56 5.541,0 10.949,-1.321 15.637,-3.82 l 0.885,-0.472 1.725,3.244 14.828,0 0,-0.794 -6.68,-11.039 0.334,-0.526 z m -26.514,0.053 c -8.094,0 -16.805,-5.697 -16.805,-18.206 0,-12.415 8.711,-18.069 16.805,-18.069 8.094,0 16.807,5.654 16.807,18.069 0,12.508 -8.713,18.206 -16.807,18.206 z" /></g><path d="m 542.927,603.421 c 3.252,-5.109 4.9,-11.218 4.9,-18.153 0,-21.831 -16.283,-31.774 -31.414,-31.774 -15.129,0 -31.414,9.943 -31.414,31.774 0,21.683 16.174,31.56 31.199,31.56 5.541,0 10.949,-1.321 15.637,-3.82 l 0.885,-0.472 1.725,3.244 h 14.828 v -0.794 l -6.68,-11.039 z m -26.514,0.053 c -8.094,0 -16.805,-5.697 -16.805,-18.206 0,-12.415 8.711,-18.069 16.805,-18.069 8.094,0 16.807,5.654 16.807,18.069 0,12.508 -8.713,18.206 -16.807,18.206 z" /></g><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path431" id="path431"
d="m 165.162,221.756 0.006,-0.025 C 140.75,212.522 116.912,200.02 94.189,184.475 69.636,167.678 51.56,151.283 37.304,132.879 28.589,121.632 23.536,112.162 20.923,102.174 c -3.068,-11.729 0.105,-20.54 9.178,-25.482 2.277,-1.241 4.834,-2.269 7.596,-3.054 7.576,-2.153 15.721,-2.812 25.201,-2.015 1.244,0.104 2.52,0.217 3.805,0.332 1.402,0.123 2.803,0.242 4.209,0.368 l 3.176,0.281 3.846,-13.919 c -0.947,-0.121 -1.893,-0.245 -2.83,-0.37 -2.537,-0.337 -4.934,-0.656 -7.25,-0.857 -4.689,-0.406 -8.803,-0.604 -12.578,-0.604 -8.74,0 -16.342,1.076 -23.24,3.29 -14.58,4.68 -23.049,13.281 -25.893,26.297 -1.943,8.9 -0.568,18.38 4.328,29.833 6.098,14.267 15.623,27.692 29.977,42.251 31.707,32.162 69.879,56.911 116.699,75.662 3.182,1.274 6.383,2.416 9.771,3.624 1.434,0.511 2.889,1.029 4.369,1.568 l 2.396,-8.365 -8.521,-9.258 z" /><path d="m 165.162,221.756 0.006,-0.025 C 140.75,212.522 116.912,200.02 94.189,184.475 69.636,167.678 51.56,151.283 37.304,132.879 28.589,121.632 23.536,112.162 20.923,102.174 c -3.068,-11.729 0.105,-20.54 9.178,-25.482 2.277,-1.241 4.834,-2.269 7.596,-3.054 7.576,-2.153 15.721,-2.812 25.201,-2.015 1.244,0.104 2.52,0.217 3.805,0.332 1.402,0.123 2.803,0.242 4.209,0.368 l 3.176,0.281 3.846,-13.919 c -0.947,-0.121 -1.893,-0.245 -2.83,-0.37 -2.537,-0.337 -4.934,-0.656 -7.25,-0.857 -4.689,-0.406 -8.803,-0.604 -12.578,-0.604 -8.74,0 -16.342,1.076 -23.24,3.29 -14.58,4.68 -23.049,13.281 -25.893,26.297 -1.943,8.9 -0.568,18.38 4.328,29.833 6.098,14.267 15.623,27.692 29.977,42.251 31.707,32.162 69.879,56.911 116.699,75.662 3.182,1.274 6.383,2.416 9.771,3.624 1.434,0.511 2.889,1.029 4.369,1.568 l 2.396,-8.365 z" /><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path433" id="path433"
d="m 279.656,208.102 c -0.146,-0.262 -0.314,-0.56 -0.359,-0.905 -0.99,-8.005 -3.834,-16.142 -8.688,-24.875 -7.945,-14.297 -18.83,-27.683 -34.252,-42.126 -3.812,-3.572 -7.723,-6.949 -11.863,-10.523 -1.678,-1.448 -3.377,-2.915 -5.096,-4.419 -0.006,0.032 -0.012,0.062 -0.018,0.092 -0.062,0.355 -0.096,0.551 -0.09,0.713 l 0.148,3.794 c 0.176,4.559 0.359,9.272 0.67,13.896 0.047,0.706 0.615,1.672 1.52,2.583 2.135,2.144 4.346,4.286 6.484,6.358 3.807,3.687 7.742,7.5 11.389,11.467 11.611,12.634 19.076,24.245 23.488,36.543 2.049,5.705 2.707,10.802 2.012,15.581 -1.146,7.896 -6.145,13.235 -15.281,16.322 -2.455,0.829 -5.002,1.474 -7.656,1.956 l 9.738,12.6 c 1.551,-0.468 3.08,-0.975 4.574,-1.562 12.387,-4.858 19.754,-12.956 22.521,-24.758 l 0.869,-3.686 0,-8.847 c -0.034,-0.068 -0.071,-0.136 -0.11,-0.204 z" /><g d="m 279.656,208.102 c -0.146,-0.262 -0.314,-0.56 -0.359,-0.905 -0.99,-8.005 -3.834,-16.142 -8.688,-24.875 -7.945,-14.297 -18.83,-27.683 -34.252,-42.126 -3.812,-3.572 -7.723,-6.949 -11.863,-10.523 -1.678,-1.448 -3.377,-2.915 -5.096,-4.419 -0.006,0.032 -0.012,0.062 -0.018,0.092 -0.062,0.355 -0.096,0.551 -0.09,0.713 l 0.148,3.794 c 0.176,4.559 0.359,9.272 0.67,13.896 0.047,0.706 0.615,1.672 1.52,2.583 2.135,2.144 4.346,4.286 6.484,6.358 3.807,3.687 7.742,7.5 11.389,11.467 11.611,12.634 19.076,24.245 23.488,36.543 2.049,5.705 2.707,10.802 2.012,15.581 -1.146,7.896 -6.145,13.235 -15.281,16.322 -2.455,0.829 -5.002,1.474 -7.656,1.956 l 9.738,12.6 c 1.551,-0.468 3.08,-0.975 4.574,-1.562 12.387,-4.858 19.754,-12.956 22.521,-24.758 l 0.869,-3.686 v -8.847 c -0.034,-0.068 -0.071,-0.136 -0.11,-0.204 z" /><g
style="fill:#ffffff" style="fill:#ffffff"
id="g435" id="g435"
transform="translate(-250.847,-184.784)"><path transform="translate(-250.847,-184.784)"><path
@ -146,19 +146,14 @@
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path439" id="path439"
d="m 433.437,425.474 c -2.322,7.348 -4.98,14.184 -8.043,20.678 -3.967,8.416 -9.191,17.993 -17.877,25.219 -9.297,7.733 -19.082,7.701 -28.365,-0.092 -5.934,-4.982 -10.92,-11.633 -15.691,-20.929 -6.629,-12.926 -11.459,-27.311 -15.66,-46.642 l -0.072,-0.342 c -0.174,-0.828 -0.412,-1.962 -0.893,-2.284 -4.152,-2.786 -8.357,-5.448 -12.807,-8.267 -1.068,-0.677 -2.146,-1.359 -3.238,-2.054 0.164,0.969 0.32,1.911 0.475,2.834 0.434,2.596 0.842,5.047 1.303,7.478 4.703,24.702 10.705,42.76 19.463,58.551 7.541,13.604 17.859,28.05 37.209,32.08 l 8.318,0 c 17.949,-3.632 27.887,-16.568 35.24,-28.748 1.953,-3.234 3.717,-6.507 5.244,-9.726 2.389,-5.035 4.557,-10.249 6.533,-15.655 l -11.139,-12.101 z" /></g><path d="m 433.437,425.474 c -2.322,7.348 -4.98,14.184 -8.043,20.678 -3.967,8.416 -9.191,17.993 -17.877,25.219 -9.297,7.733 -19.082,7.701 -28.365,-0.092 -5.934,-4.982 -10.92,-11.633 -15.691,-20.929 -6.629,-12.926 -11.459,-27.311 -15.66,-46.642 l -0.072,-0.342 c -0.174,-0.828 -0.412,-1.962 -0.893,-2.284 -4.152,-2.786 -8.357,-5.448 -12.807,-8.267 -1.068,-0.677 -2.146,-1.359 -3.238,-2.054 0.164,0.969 0.32,1.911 0.475,2.834 0.434,2.596 0.842,5.047 1.303,7.478 4.703,24.702 10.705,42.76 19.463,58.551 7.541,13.604 17.859,28.05 37.209,32.08 h 8.318 c 17.949,-3.632 27.887,-16.568 35.24,-28.748 1.953,-3.234 3.717,-6.507 5.244,-9.726 2.389,-5.035 4.557,-10.249 6.533,-15.655 z" /></g><path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#ffffff" style="fill:#ffffff"
id="path493" id="path493"
d="M 28.084,368.98 0,429.872 l 0,1.124 14.16,0 4.202,-8.945 25.208,0 4.195,8.945 14.16,0 0,-1.124 -28.172,-60.892 -5.669,0 z m -5.438,41.259 8.215,-19.134 8.424,19.134 -16.639,0 z" /><g d="M 28.084,368.98 0,429.872 v 1.124 h 14.16 l 4.202,-8.945 H 43.57 l 4.195,8.945 h 14.16 v -1.124 L 33.753,368.98 Z m -5.438,41.259 8.215,-19.134 8.424,19.134 z" /><g
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text3371"><path id="text3371"><path
d="m 318.27705,360.24104 c 2.295,-0.315 4.95,-2.115 4.95,-5.895 0,-3.96 -2.88,-6.57 -8.775,-6.57 -4.635,0 -8.685,1.62 -9.675,6.57 l 4.275,1.17 c 0.27,-1.98 1.395,-3.87 4.95,-3.87 2.925,0 4.635,1.305 4.635,3.51 0,2.295 -1.755,3.555 -4.77,3.555 l -2.475,0 0,3.42 2.655,0 c 2.925,0 4.86,1.215 4.86,3.69 0,2.295 -1.62,4.095 -4.86,4.095 -3.06,0 -5.04,-1.53 -5.625,-4.275 l -4.14,1.17 c 0.9,4.59 4.68,7.155 9.9,7.155 5.715,0 9.315,-3.015 9.315,-7.515 0,-3.69 -2.385,-5.76 -5.22,-6.03 l 0,-0.18 z" d="m 318.27705,360.24104 c 2.295,-0.315 4.95,-2.115 4.95,-5.895 0,-3.96 -2.88,-6.57 -8.775,-6.57 -4.635,0 -8.685,1.62 -9.675,6.57 l 4.275,1.17 c 0.27,-1.98 1.395,-3.87 4.95,-3.87 2.925,0 4.635,1.305 4.635,3.51 0,2.295 -1.755,3.555 -4.77,3.555 h -2.475 v 3.42 h 2.655 c 2.925,0 4.86,1.215 4.86,3.69 0,2.295 -1.62,4.095 -4.86,4.095 -3.06,0 -5.04,-1.53 -5.625,-4.275 l -4.14,1.17 c 0.9,4.59 4.68,7.155 9.9,7.155 5.715,0 9.315,-3.015 9.315,-7.515 0,-3.69 -2.385,-5.76 -5.22,-6.03 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff" style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
id="path3376" /><path id="path3376"
d="m 330.56416,373.87604 c 1.89,0 2.88,-1.17 2.88,-2.7 0,-1.485 -0.99,-2.655 -2.88,-2.655 -1.89,0 -2.925,1.17 -2.925,2.655 0,1.485 0.99,2.7 2.925,2.7 z" inkscape:connector-curvature="0" /></g></svg>
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
id="path3378" /><path
d="m 346.79791,347.77604 c -6.39,0 -10.26,4.455 -10.26,13.095 0,8.64 3.87,13.095 10.26,13.095 6.345,0 10.215,-4.455 10.215,-13.095 0,-8.64 -3.87,-13.095 -10.215,-13.095 z m 0,4.32 c 3.78,0 5.49,2.79 5.49,8.775 0,5.94 -1.71,8.775 -5.49,8.775 -3.825,0 -5.535,-2.835 -5.535,-8.775 0,-5.985 1.71,-8.775 5.535,-8.775 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
id="path3380" /></g></svg>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -223,7 +223,7 @@ class ScanWidget(QtWidgets.QWidget):
self._zoom(self.zoomFactor**y, ev.x()) self._zoom(self.zoomFactor**y, ev.x())
def resizeEvent(self, ev): def resizeEvent(self, ev):
if not ev.oldSize().isValid(): if not ev.oldSize().isValid() or not ev.oldSize().width():
self.viewRange() self.viewRange()
return return
self.ticker.min_ticks = max( self.ticker.min_ticks = max(

View File

@ -117,7 +117,7 @@ def syscall(arg=None, flags={}):
def inner_decorator(function): def inner_decorator(function):
function.artiq_embedded = \ function.artiq_embedded = \
_ARTIQEmbeddedInfo(core_name=None, portable=False, function=None, _ARTIQEmbeddedInfo(core_name=None, portable=False, function=None,
syscall=function.__name__, forbidden=False, syscall=arg, forbidden=False,
flags=set(flags)) flags=set(flags))
return function return function
return inner_decorator return inner_decorator

View File

@ -42,12 +42,10 @@ class NoScan(ScanObject):
self.value = value self.value = value
self.repetitions = repetitions self.repetitions = repetitions
@portable
def _gen(self): def _gen(self):
for i in range(self.repetitions): for i in range(self.repetitions):
yield self.value yield self.value
@portable
def __iter__(self): def __iter__(self):
return self._gen() return self._gen()
@ -81,7 +79,6 @@ class RangeScan(ScanObject):
rng = random.Random(seed) rng = random.Random(seed)
random.shuffle(self.sequence, rng.random) random.shuffle(self.sequence, rng.random)
@portable
def __iter__(self): def __iter__(self):
return iter(self.sequence) return iter(self.sequence)
@ -101,7 +98,6 @@ class ExplicitScan(ScanObject):
def __init__(self, sequence): def __init__(self, sequence):
self.sequence = sequence self.sequence = sequence
@portable
def __iter__(self): def __iter__(self):
return iter(self.sequence) return iter(self.sequence)

View File

@ -1,4 +1,5 @@
import asyncio import asyncio
import tokenize
from artiq.protocols.sync_struct import Notifier, process_mod from artiq.protocols.sync_struct import Notifier, process_mod
from artiq.protocols import pyon from artiq.protocols import pyon
@ -7,7 +8,7 @@ from artiq.tools import TaskObject
def device_db_from_file(filename): def device_db_from_file(filename):
glbs = dict() glbs = dict()
with open(filename, "r") as f: with tokenize.open(filename) as f:
exec(f.read(), glbs) exec(f.read(), glbs)
return glbs["device_db"] return glbs["device_db"]

View File

@ -152,23 +152,29 @@ class ExamineDatasetMgr:
def examine(device_mgr, dataset_mgr, file): def examine(device_mgr, dataset_mgr, file):
module = file_import(file) previous_keys = set(sys.modules.keys())
for class_name, exp_class in module.__dict__.items(): try:
if class_name[0] == "_": module = file_import(file)
continue for class_name, exp_class in module.__dict__.items():
if is_experiment(exp_class): if class_name[0] == "_":
if exp_class.__doc__ is None: continue
name = class_name if is_experiment(exp_class):
else: if exp_class.__doc__ is None:
name = exp_class.__doc__.splitlines()[0].strip() name = class_name
if name[-1] == ".": else:
name = name[:-1] name = exp_class.__doc__.splitlines()[0].strip()
argument_mgr = TraceArgumentManager() if name[-1] == ".":
exp_class((device_mgr, dataset_mgr, argument_mgr)) name = name[:-1]
arginfo = OrderedDict( argument_mgr = TraceArgumentManager()
(k, (proc.describe(), group, tooltip)) exp_class((device_mgr, dataset_mgr, argument_mgr))
for k, (proc, group, tooltip) in argument_mgr.requested_args.items()) arginfo = OrderedDict(
register_experiment(class_name, name, arginfo) (k, (proc.describe(), group, tooltip))
for k, (proc, group, tooltip) in argument_mgr.requested_args.items())
register_experiment(class_name, name, arginfo)
finally:
new_keys = set(sys.modules.keys())
for key in new_keys - previous_keys:
del sys.modules[key]
def setup_diagnostics(experiment_file, repository_path): def setup_diagnostics(experiment_file, repository_path):

View File

@ -1,13 +1,37 @@
import sys import sys
import socket
__all__ = [] __all__ = []
if sys.version_info[:3] >= (3, 5, 2): if sys.version_info[:3] >= (3, 5, 2) and sys.version_info[:3] <= (3, 6, 6):
import asyncio import asyncio
# See https://github.com/m-labs/artiq/issues/506 # See https://github.com/m-labs/artiq/issues/506
def _ipaddr_info(host, port, family, type, proto): def _ipaddr_info(host, port, family, type, proto):
return None return None
asyncio.base_events._ipaddr_info = _ipaddr_info asyncio.base_events._ipaddr_info = _ipaddr_info
# See https://github.com/m-labs/artiq/issues/1016
@asyncio.coroutine
def sock_connect(self, sock, address):
"""Connect to a remote socket at address.
This method is a coroutine.
"""
if self._debug and sock.gettimeout() != 0:
raise ValueError("the socket must be non-blocking")
if not hasattr(socket, 'AF_UNIX') or sock.family != socket.AF_UNIX:
socktype = sock.type & 0xf # WA https://bugs.python.org/issue21327
resolved = asyncio.base_events._ensure_resolved(
address, family=sock.family, type=socktype, proto=sock.proto, loop=self)
if not resolved.done():
yield from resolved
_, _, _, _, address = resolved.result()[0]
fut = self.create_future()
self._sock_connect(fut, sock, address)
return (yield from fut)
asyncio.selector_events.BaseSelectorEventLoop.sock_connect = sock_connect

View File

@ -168,6 +168,7 @@ class LogForwarder(logging.Handler, TaskObject):
self._queue.put_nowait(record.source + ":" + self.format(record)) self._queue.put_nowait(record.source + ":" + self.format(record))
async def _do(self): async def _do(self):
reader = writer = None
while True: while True:
try: try:
reader, writer = await asyncio.open_connection(self.host, reader, writer = await asyncio.open_connection(self.host,
@ -182,4 +183,5 @@ class LogForwarder(logging.Handler, TaskObject):
except: except:
await asyncio.sleep(self.reconnect_timer) await asyncio.sleep(self.reconnect_timer)
finally: finally:
writer.close() if writer is not None:
writer.close()

View File

@ -243,7 +243,8 @@ class AsyncioClient:
No further method calls should be done after this method is called. No further method calls should be done after this method is called.
""" """
self.__writer.close() if self.__writer is not None:
self.__writer.close()
self.__reader = None self.__reader = None
self.__writer = None self.__writer = None
self.__target_names = None self.__target_names = None

View File

@ -188,6 +188,7 @@ _eval_dict = {
"false": False, "false": False,
"true": True, "true": True,
"slice": slice, "slice": slice,
"nan": numpy.nan,
"Fraction": Fraction, "Fraction": Fraction,
"OrderedDict": OrderedDict, "OrderedDict": OrderedDict,

View File

@ -74,4 +74,4 @@ class AnalyzerTest(ExperimentCase):
log = "".join([_extract_log_chars(msg.data) log = "".join([_extract_log_chars(msg.data)
for msg in dump.messages for msg in dump.messages
if isinstance(msg, OutputMessage) and msg.channel == dump.log_channel]) if isinstance(msg, OutputMessage) and msg.channel == dump.log_channel])
self.assertEqual(log, "foo\x1E32\n\x1D") self.assertEqual(log, "foo\x1E32\x1D")

View File

@ -140,6 +140,8 @@ class _RPCTypes(EnvExperiment):
self.accept("foo") self.accept("foo")
self.accept(b"foo") self.accept(b"foo")
self.accept(bytearray(b"foo")) self.accept(bytearray(b"foo"))
self.accept(bytes([1, 2]))
self.accept(bytearray([1, 2]))
self.accept((2, 3)) self.accept((2, 3))
self.accept([1, 2]) self.accept([1, 2])
self.accept(range(10)) self.accept(range(10))
@ -211,10 +213,25 @@ class _RPCCalls(EnvExperiment):
def numpy_full(self): def numpy_full(self):
return numpy.full(10, 20) return numpy.full(10, 20)
@kernel
def numpy_nan(self):
return numpy.full(10, numpy.nan)
@kernel @kernel
def builtin(self): def builtin(self):
sleep(1.0) sleep(1.0)
@rpc(flags={"async"})
def async_rpc(self):
pass
@kernel
def async_in_try(self):
try:
self.async_rpc()
except ValueError:
pass
class RPCCallsTest(ExperimentCase): class RPCCallsTest(ExperimentCase):
def test_args(self): def test_args(self):
@ -229,7 +246,9 @@ class RPCCallsTest(ExperimentCase):
self.assertEqual(exp.numpy_things(), self.assertEqual(exp.numpy_things(),
(numpy.int32(10), numpy.int64(20), numpy.array([42,]))) (numpy.int32(10), numpy.int64(20), numpy.array([42,])))
self.assertTrue((exp.numpy_full() == numpy.full(10, 20)).all()) self.assertTrue((exp.numpy_full() == numpy.full(10, 20)).all())
self.assertTrue(numpy.isnan(exp.numpy_nan()).all())
exp.builtin() exp.builtin()
exp.async_in_try()
class _Annotation(EnvExperiment): class _Annotation(EnvExperiment):

View File

@ -0,0 +1,83 @@
import os
import unittest
import time
from artiq.experiment import *
from artiq.test.hardware_testbench import ExperimentCase
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
class _Transfer(EnvExperiment):
def build(self):
self.setattr_device("core")
self.data = b"\x00"*(10**6)
@rpc
def source(self) -> TBytes:
return self.data
@rpc(flags={"async"})
def sink(self, data):
assert data == self.data
@kernel
def host_to_device(self):
t0 = self.core.get_rtio_counter_mu()
data = self.source()
t1 = self.core.get_rtio_counter_mu()
return len(data)/self.core.mu_to_seconds(t1-t0)
@kernel
def device_to_host(self):
t0 = self.core.get_rtio_counter_mu()
self.sink(self.data)
t1 = self.core.get_rtio_counter_mu()
return len(self.data)/self.core.mu_to_seconds(t1-t0)
class TransferTest(ExperimentCase):
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
def test_host_to_device(self):
exp = self.create(_Transfer)
host_to_device_rate = exp.host_to_device()
print(host_to_device_rate, "B/s")
self.assertGreater(host_to_device_rate, 1.6e6)
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
def test_device_to_host(self):
exp = self.create(_Transfer)
device_to_host_rate = exp.device_to_host()
print(device_to_host_rate, "B/s")
self.assertGreater(device_to_host_rate, 1.6e6)
class _KernelOverhead(EnvExperiment):
def build(self):
self.setattr_device("core")
def kernel_overhead(self):
n = 100
t0 = time.monotonic()
for _ in range(n):
self.dummy_kernel()
t1 = time.monotonic()
return (t1-t0)/n
@kernel
def dummy_kernel(self):
pass
class KernelOverheadTest(ExperimentCase):
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
def test_kernel_overhead(self):
exp = self.create(_KernelOverhead)
kernel_overhead = exp.kernel_overhead()
print(kernel_overhead, "s")
self.assertGreater(kernel_overhead, 0.001)
self.assertLess(kernel_overhead, 0.5)

View File

@ -202,6 +202,20 @@ class _RPCExceptions(EnvExperiment):
self.success = True self.success = True
class _Keywords(EnvExperiment):
def build(self, value, output):
self.setattr_device("core")
self.value = value
self.output = output
def rpc(self, kw):
self.output.append(kw)
@kernel
def run(self):
self.rpc(kw=self.value)
class HostVsDeviceCase(ExperimentCase): class HostVsDeviceCase(ExperimentCase):
def test_primes(self): def test_primes(self):
l_device, l_host = [], [] l_device, l_host = [], []
@ -245,3 +259,18 @@ class HostVsDeviceCase(ExperimentCase):
f(_RPCExceptions, catch=False) f(_RPCExceptions, catch=False)
uut = self.execute(_RPCExceptions, catch=True) uut = self.execute(_RPCExceptions, catch=True)
self.assertTrue(uut.success) self.assertTrue(uut.success)
def test_keywords(self):
for f in self.execute, _run_on_host:
output = []
f(_Keywords, value=0, output=output)
self.assertEqual(output, [0])
output = []
f(_Keywords, value=1, output=output)
self.assertEqual(output, [1])
output = []
f(_Keywords, value=False, output=output)
self.assertEqual(output, [False])
output = []
f(_Keywords, value=True, output=output)
self.assertEqual(output, [True])

View File

@ -81,7 +81,7 @@ class ClockGeneratorLoopback(EnvExperiment):
self.core.reset() self.core.reset()
self.loop_clock_in.input() self.loop_clock_in.input()
self.loop_clock_out.stop() self.loop_clock_out.stop()
delay(20*us) delay(200*us)
with parallel: with parallel:
self.loop_clock_in.gate_rising(10*us) self.loop_clock_in.gate_rising(10*us)
with sequential: with sequential:
@ -178,6 +178,61 @@ class LoopbackCount(EnvExperiment):
self.set_dataset("count", self.loop_in.count()) self.set_dataset("count", self.loop_in.count())
class IncorrectPulseTiming(Exception):
pass
class LoopbackGateTiming(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("loop_in")
self.setattr_device("loop_out")
@kernel
def run(self):
# Make sure there are no leftover events.
self.core.reset()
# Determine loop delay.
with parallel:
self.loop_in.gate_rising_mu(10000)
with sequential:
delay_mu(5000)
out_mu = now_mu()
self.loop_out.pulse_mu(1000)
in_mu = self.loop_in.timestamp_mu()
if in_mu < 0:
raise PulseNotReceived("Cannot determine loop delay")
loop_delay_mu = in_mu - out_mu
# With the exact delay known, make sure tight gate timings work.
# In the most common configuration, 24 mu == 24 ns == 3 coarse periods,
# which should be plenty of slack.
delay_mu(10000)
gate_start_mu = now_mu()
self.loop_in.gate_both_mu(24)
gate_end_mu = now_mu()
# gateware latency offset between gate and input
lat_offset = 3*8
out_mu = gate_start_mu - loop_delay_mu + lat_offset
at_mu(out_mu)
self.loop_out.pulse_mu(24)
in_mu = self.loop_in.timestamp_mu()
if in_mu < 0:
raise PulseNotReceived()
if not (gate_start_mu <= (in_mu - lat_offset) <= gate_end_mu):
raise IncorrectPulseTiming("Input event should occur during gate")
if not (-2 < (in_mu - out_mu - loop_delay_mu) < 2):
raise IncorrectPulseTiming("Loop delay should not change")
in_mu = self.loop_in.timestamp_mu()
if in_mu > 0:
raise IncorrectPulseTiming("Only one pulse should be received")
class IncorrectLevel(Exception): class IncorrectLevel(Exception):
pass pass
@ -387,6 +442,9 @@ class CoredeviceTest(ExperimentCase):
count = self.dataset_mgr.get("count") count = self.dataset_mgr.get("count")
self.assertEqual(count, npulses) self.assertEqual(count, npulses)
def test_loopback_gate_timing(self):
self.execute(LoopbackGateTiming)
def test_level(self): def test_level(self):
self.execute(Level) self.execute(Level)

View File

@ -0,0 +1,29 @@
import os
import time
import unittest
from artiq.experiment import *
from artiq.test.hardware_testbench import ExperimentCase
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
class _Stress(EnvExperiment):
def build(self):
self.setattr_device("core")
@rpc(flags={"async"})
def sink(self, data):
pass
@kernel
def async_rpc(self, n):
for _ in range(n):
self.sink(b"")
class StressTest(ExperimentCase):
def test_async_rpc(self):
exp = self.create(_Stress)
exp.async_rpc(16000)

View File

@ -34,14 +34,33 @@ mod cslice {
} }
} }
} }
pub trait AsCSlice<'a, T> {
fn as_c_slice(&'a self) -> CSlice<'a, T>;
}
impl<'a> AsCSlice<'a, u8> for str {
fn as_c_slice(&'a self) -> CSlice<'a, u8> {
CSlice {
base: self.as_ptr(),
len: self.len() as u32,
phantom: PhantomData
}
}
}
} }
#[path = "../../firmware/ksupport/eh.rs"] #[path = "."]
pub mod eh; pub mod eh {
#[path = "../../firmware/libeh/dwarf.rs"]
pub mod dwarf;
}
#[path = "../../firmware/ksupport/eh_artiq.rs"]
pub mod eh_artiq;
use std::{str, process}; use std::{str, process};
fn terminate(exception: &eh::Exception, mut _backtrace: &mut [usize]) -> ! { fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> ! {
println!("Uncaught {}: {} ({}, {}, {})", println!("Uncaught {}: {} ({}, {}, {})",
str::from_utf8(exception.name.as_ref()).unwrap(), str::from_utf8(exception.name.as_ref()).unwrap(),
str::from_utf8(exception.message.as_ref()).unwrap(), str::from_utf8(exception.message.as_ref()).unwrap(),

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