diff --git a/docs/Project.toml b/docs/Project.toml index dfa65cd1..1814eb33 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,2 +1,5 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" + +[compat] +Documenter = "1" diff --git a/docs/make.jl b/docs/make.jl index 95029870..085f2756 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -27,7 +27,8 @@ makedocs( "FPGA Development" => "fpga.md", "Development Tips" => "devtips.md", #"Getting Started" => "overview.md", - ] + ], + warnonly = [:missing_docs] # html_prettyurls = false, #!("local" in ARGS), ) diff --git a/docs/src/acquisition.md b/docs/src/acquisition.md index 63aaf058..48d92b4e 100644 --- a/docs/src/acquisition.md +++ b/docs/src/acquisition.md @@ -1,6 +1,6 @@ -# Data Acqusition +# Data Acquisition -The data acqusition of the RedPitayaDAQServer project is based on two data flows to and from the upper 128 MB of the RedPitaya memory. This memory region acts as a ring buffer for the acquired samples and can be queried by clients using SCPI commands. +The data acquisition of the RedPitayaDAQServer project is based on two data flows to and from the upper 128 MB of the RedPitaya memory. This memory region acts as a ring buffer for the acquired samples and can be queried by clients using SCPI commands. Signal acquisition within a cluster is based on a shared clock and trigger signal distributed via cables between the RedPitayas. Once triggered, all FPGAs continuously write the samples from their ADC channel to the sample ring-buffer with each clock tick. Both ADC channels on a RedPitaya are written to the buffer at the same time. The 14-bit values of the ADCs are converted to 16-bit signed integer samples and then concatenated into one 32-bit value, which is then written to the buffer. The sampling rate of the system can be adjusted by setting a decimation parameter and the decimation itself is realized with a CIC filter. @@ -43,4 +43,4 @@ At this sampling rate a single RedPitaya produces new samples at a data rate of |16|7.81|31.25|250|4.29s| |8|15.63|62.5|500|2.15s| -The table only refers to the data rate of new samples being produced. The data rate of samples being transmitted to a client can differ greatly depending on how the client queries and processes the samples and the available network bandwidth and usage. At the higher sampling rates it is recommended to have client threads that exclusively receive samples and perform any computation on samples in different threads to maximise the transmission speed, as a server can only transmit data at a rate of just above 500 Mbit/s. This exceeds the highest supported sampling rate by only a few Mbit/s and a client with frequency interruptions of its sample reception might not be able to keep up with the sampling rate. \ No newline at end of file +The table only refers to the data rate of new samples being produced. The data rate of samples being transmitted to a client can differ greatly depending on how the client queries and processes the samples and the available network bandwidth and usage. At the higher sampling rates it is recommended to have client threads that exclusively receive samples and perform any computation on samples in different threads to maximise the transmission speed, as a server can only transmit data at a rate of just above 500 Mbit/s. This exceeds the highest supported sampling rate by only a few Mbit/s and a client with frequency interruptions of its sample reception might not be able to keep up with the sampling rate. diff --git a/docs/src/client.md b/docs/src/client.md index 852bfd64..59ac95a2 100644 --- a/docs/src/client.md +++ b/docs/src/client.md @@ -2,6 +2,7 @@ This page contains documentation of the public API of the Julia client. In the Julia REPL one can access this documentation by entering the help mode with `?` and then writing the function for which the documentation should be shown. + ## Connection and Communication ```@docs RedPitayaDAQServer.RedPitaya @@ -40,6 +41,7 @@ RedPitayaDAQServer.calibADCOffset RedPitayaDAQServer.calibADCOffset! RedPitayaDAQServer.calibADCScale RedPitayaDAQServer.calibADCScale! +RedPitayaDAQServer.updateCalib! ``` ## DAC Configuration ```@docs @@ -61,12 +63,12 @@ RedPitayaDAQServer.samplesPerStep! RedPitayaDAQServer.stepsPerFrame! RedPitayaDAQServer.clearSequence! RedPitayaDAQServer.sequence! -RedPitayaDAQServer.length(::AbstractSequence) -RedPitayaDAQServer.start RedPitayaDAQServer.calibDACOffset RedPitayaDAQServer.calibDACOffset! RedPitayaDAQServer.calibDACScale RedPitayaDAQServer.calibDACScale! +RedPitayaDAQServer.calibDACUpperLimit! +RedPitayaDAQServer.calibDACLowerLimit! ``` ## Measurement and Transmission ```@docs @@ -79,11 +81,22 @@ RedPitayaDAQServer.SampleChunk RedPitayaDAQServer.PerformanceData RedPitayaDAQServer.readSamples RedPitayaDAQServer.readFrames -RedPitayaDAQServer.convertSamplesToFrames +RedPitayaDAQServer.readPeriods +RedPitayaDAQServer.convertSamplesToFrames(::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) +RedPitayaDAQServer.convertSamplesToPeriods!(::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) +``` +## Slow IO +```@autodocs +Modules = [RedPitayaDAQServer] +Pages = ["SlowIO.jl"] +``` +## EEPROM and Calibration +```@autodocs +Modules = [RedPitayaDAQServer] +Pages = ["EEPROM.jl"] +``` +## Counter Trigger +```@autodocs +Modules = [RedPitayaDAQServer] +Pages = ["CounterTrigger.jl"] ``` -## Utility -```@docs -RedPitayaDAQServer.listReleaseTags -RedPitayaDAQServer.latestReleaseTags -RedPitayaDAQServer.update! -``` \ No newline at end of file diff --git a/docs/src/generation.md b/docs/src/generation.md index 956644f2..d57cfce4 100644 --- a/docs/src/generation.md +++ b/docs/src/generation.md @@ -28,9 +28,9 @@ Comparable to the sample transmission of the acquisition, this updating of the L Sequences and their steps also have additional features. A step can be marked such that during its duration the signal is set to 0. Furthermore, a step can be marked such that it triggers the ramp down. To make this easier to manage the server actually manages three sequences, that can be set individually: A ramp up, regular and ramp down sequence. The ramp up sequence is moved to the FPGA LUT at the acquisition start, followed by the regular sequence. Afterwards the ramp down sequence is started and during its execution the ramp down flag is set. ## Calibration -Similar to the signal acqusition, there are also calibration scale ``c_{i, scale}`` and offset ``c_{i, offset}`` values for the signal generation. These are stored in the EEPROM of the RedPitaya and can be updated by a client. The calibration values are always applied, even when the master trigger is off. +Similar to the signal acquisition, there are also calibration scale ``c_{i, scale}`` and offset ``c_{i, offset}`` values for the signal generation. These are stored in the EEPROM of the RedPitaya and can be updated by a client. The calibration values are always applied, even when the master trigger is off. Thus the total signal can be described as: ```math S_i''(t) = c_{i, scale} S_i'(t) + c_{i, offset} -``` \ No newline at end of file +``` diff --git a/docs/src/installation.md b/docs/src/installation.md index 0b03b161..38e7764d 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -15,7 +15,7 @@ To finish installing the RedPitaya, simply unzip one of the releases and copy th When the RedPitaya is now booted, the server should start. One can then use a client to connect, at which point the FPGA image is loaded. -The client library provided with the project is not an executable program, but it can be used to implement one. The library encapsulates the communication with the server and implements various optimizations. As the communication with the server is language agnostic one could therefore implement their own client in a different language. The Julia reference client library found in `src/client/julia`, the [SCPI commands](scpi.md) and the sections on the signal [acquisition](acqusition.md) and [generation](generation.md) are starting points for such a custom client. +The client library provided with the project is not an executable program, but it can be used to implement one. The library encapsulates the communication with the server and implements various optimizations. As the communication with the server is language agnostic one could therefore implement their own client in a different language. The Julia reference client library found in `src/client/julia`, the [SCPI commands](scpi.md) and the sections on the signal [acquisition](acquisition.md) and [generation](generation.md) are starting points for such a custom client. ## Julia Client To use the provided Julia client library you need to install RedPitayaDAQServer Julia package within Julia. To this end @@ -148,4 +148,4 @@ dev RedPitayaDAQServer ``` in the package mode `]`. -This installs the package in development mode and puts the files into `~/.julia/dev/RedPitayaDAQServer/`. There you can the also modify the files, which is handy when trying out the examples. You need to manually `git pull` if you want to get updates, i.e. Julia will not update developed packages automatically. \ No newline at end of file +This installs the package in development mode and puts the files into `~/.julia/dev/RedPitayaDAQServer/`. There you can the also modify the files, which is handy when trying out the examples. You need to manually `git pull` if you want to get updates, i.e. Julia will not update developed packages automatically. diff --git a/docs/src/scpi.md b/docs/src/scpi.md index 32fe2328..9af44dbd 100644 --- a/docs/src/scpi.md +++ b/docs/src/scpi.md @@ -39,7 +39,7 @@ After each SCPI command the server replies with `true` or `false` on the command ## Sequence Configuration -The server maintains three acqusition sequences. When the server is in the`CONFIGURATION` mode a client can configure a set of three sequences. If the current configured sequences fits the desired signal, a client can intstruct the server to set the sequences. This moves the configuration sequences to the acquisition sequences and writes the first values to the FPGA buffer. +The server maintains three acquisition sequences. When the server is in the`CONFIGURATION` mode a client can configure a set of three sequences. If the current configured sequences fits the desired signal, a client can intstruct the server to set the sequences. This moves the configuration sequences to the acquisition sequences and writes the first values to the FPGA buffer. During an active trigger the buffer is periodically updated by the server. If the server recognizes the end of a sequence, it sets the amplitudes of the waveform components to 0. diff --git a/libs/scpi-parser b/libs/scpi-parser index 224f7cee..361fe25c 160000 --- a/libs/scpi-parser +++ b/libs/scpi-parser @@ -1 +1 @@ -Subproject commit 224f7cee7c2c5592081e1dec640862f029323782 +Subproject commit 361fe25cb1fe4cacb4f7494944c5d4084eb07f61 diff --git a/src/client/julia/Project.toml b/src/client/julia/Project.toml index 6c278017..cb0a62b7 100644 --- a/src/client/julia/Project.toml +++ b/src/client/julia/Project.toml @@ -1,7 +1,7 @@ name = "RedPitayaDAQServer" uuid = "c544963a-496b-56d4-a5fe-f99a3f174c8f" authors = ["Tobias Knopp "] -version = "0.6.0" +version = "0.8.0" [deps] DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" @@ -11,7 +11,13 @@ Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] +Aqua = "0.8" DocStringExtensions = "0.8, 0.9" +LinearAlgebra = "1" +Random = "1" +Statistics = "1" +Sockets = "1" +Test = "1" julia = "1.7" [extras] diff --git a/src/client/julia/src/ADC.jl b/src/client/julia/src/ADC.jl index 579f33eb..e270874e 100644 --- a/src/client/julia/src/ADC.jl +++ b/src/client/julia/src/ADC.jl @@ -63,6 +63,9 @@ function RPStatus(statusRaw::Integer) ) end +""" +Holds the performance data that is used for monitoring. +""" struct PerformanceData wpRead::UInt64 adc::ADCPerformanceData diff --git a/src/client/julia/src/Acquisition.jl b/src/client/julia/src/Acquisition.jl index ae630130..fb08c1ba 100644 --- a/src/client/julia/src/Acquisition.jl +++ b/src/client/julia/src/Acquisition.jl @@ -268,25 +268,13 @@ function readFrames( return data end -function convertSamplesToFrames( - rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, - samples, - numChan, - numSampPerPeriod, - numPeriods, - numFrames, - numBlockAverages = 1, - numPeriodsPerPatch = 1, -) - frames = convertSamplesToFrames( - samples, - numChan, - numSampPerPeriod, - numPeriods, - numFrames, - numBlockAverages, - numPeriodsPerPatch, - ) +""" +Converts a given set of samples to frames. + +See [`readFrames`](@ref) +""" +function convertSamplesToFrames(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages=1, numPeriodsPerPatch=1) + frames = convertSamplesToFrames(samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages, numPeriodsPerPatch) calibs = [x.calib for x ∈ rpu] calib = hcat(calibs...) for d ∈ 1:size(frames, 2) @@ -296,16 +284,13 @@ function convertSamplesToFrames( return frames end -function convertSamplesToFrames( - samples, - numChan, - numSampPerPeriod, - numPeriods, - numFrames, - numBlockAverages = 1, - numPeriodsPerPatch = 1, -) - if rem(numSampPerPeriod, numBlockAverages) != 0 +""" +Converts a given set of samples to frames. + +See [`readFrames`](@ref) +""" +function convertSamplesToFrames(samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages=1, numPeriodsPerPatch=1) + if rem(numSampPerPeriod,numBlockAverages) != 0 error("block averages has to be a divider of numSampPerPeriod") end numTrueSampPerPeriod = div(numSampPerPeriod, numBlockAverages * numPeriodsPerPatch) @@ -324,29 +309,13 @@ function convertSamplesToFrames( return frames end -function convertSamplesToFrames!( - rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, - samples, - frames, - numChan, - numSampPerPeriod, - numPeriods, - numFrames, - numTrueSampPerPeriod, - numBlockAverages = 1, - numPeriodsPerPatch = 1, -) - convertSamplesToFrames!( - samples, - frames, - numChan, - numSampPerPeriod, - numPeriods, - numFrames, - numTrueSampPerPeriod, - numBlockAverages, - numPeriodsPerPatch, - ) +""" +Converts a given set of samples to frames in-place. + +See [`readFrames`](@ref) +""" +function convertSamplesToFrames!(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages=1, numPeriodsPerPatch=1) + convertSamplesToFrames!(samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages, numPeriodsPerPatch) calibs = [x.calib for x ∈ rpu] calib = hcat(calibs...) for d ∈ 1:size(frames, 2) @@ -355,17 +324,12 @@ function convertSamplesToFrames!( end end -function convertSamplesToFrames!( - samples, - frames, - numChan, - numSampPerPeriod, - numPeriods, - numFrames, - numTrueSampPerPeriod, - numBlockAverages = 1, - numPeriodsPerPatch = 1, -) +""" +Converts a given set of samples to frames in-place. + +See [`readFrames`](@ref) +""" +function convertSamplesToFrames!(samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages=1, numPeriodsPerPatch=1) temp = reshape(samples, numChan, numSampPerPeriod, numPeriods, numFrames) for d ∈ 1:div(numChan, 2) u = temp[(2 * d - 1):(2 * d), :, :, :] @@ -440,15 +404,12 @@ function readPeriods( return data end -function convertSamplesToPeriods!( - rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, - samples, - periods, - numChan, - numSampPerPeriod, - numPeriods, - numBlockAverages = 1, -) +""" +Converts a given set of samples to periods in-place. + +See [`readPeriods`](@ref) +""" +function convertSamplesToPeriods!(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages=1) convertSamplesToPeriods!(samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages) calibs = [x.calib for x ∈ rpu] calib = hcat(calibs...) @@ -458,14 +419,13 @@ function convertSamplesToPeriods!( end return periods end -function convertSamplesToPeriods!( - samples, - periods, - numChan, - numSampPerPeriod, - numPeriods, - numBlockAverages = 1, -) + +""" +Converts a given set of samples to periods in-place. + +See [`readPeriods`](@ref) +""" +function convertSamplesToPeriods!(samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages=1) temp = reshape(samples, numChan, numSampPerPeriod, numPeriods) for d ∈ 1:div(numChan, 2) u = temp[(2 * d - 1):(2 * d), :, :] diff --git a/src/client/julia/src/EEPROM.jl b/src/client/julia/src/EEPROM.jl index 58f01b61..84d3fb70 100644 --- a/src/client/julia/src/EEPROM.jl +++ b/src/client/julia/src/EEPROM.jl @@ -1,13 +1,4 @@ -export calibDACOffset, - calibDACOffset!, - calibADCScale, - calibADCScale!, - calibADCOffset, - calibADCOffset!, - updateCalib!, - calibDACScale!, - calibDACScale, - calibFlags +export calibDACOffset, calibDACOffset!, calibADCScale, calibADCScale!, calibADCOffset, calibADCOffset!, updateCalib!, calibDACScale!, calibDACScale, calibFlags, calibDACLowerLimit, calibDACLowerLimit!, calibDACUpperLimit, calibDACUpperLimit!, calibDACLimit! """ Store calibration DAC offset `val` for given channel into the RedPitayas EEPROM. @@ -23,6 +14,7 @@ function scpiCommand(::typeof(calibDACOffset!), channel::Integer, val) return string("RP:CALib:DAC:CH", Int(channel) - 1, ":OFF $(Float32(val))") end scpiReturn(::typeof(calibDACOffset!)) = Bool + """ Retrieve the calibration DAC offset for given channel from the RedPitayas EEPROM """ @@ -43,6 +35,7 @@ function scpiCommand(::typeof(calibDACScale!), channel::Integer, val) return string("RP:CALib:DAC:CH", Int(channel) - 1, ":SCA $(Float32(val))") end scpiReturn(::typeof(calibDACScale!)) = Bool + """ Retrieve the calibration DAC scale for given channel from the RedPitayas EEPROM. """ @@ -52,11 +45,57 @@ end scpiCommand(::typeof(calibDACScale), channel::Integer) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":SCA?") scpiReturn(::typeof(calibDACScale)) = Float64 +""" +Store calibration DAC lower limit `val` for given channel into the RedPitayas EEPROM. +This value is used by the server to limit the output voltage. +""" +function calibDACLowerLimit!(rp::RedPitaya, channel::Integer, val) + return query(rp, scpiCommand(calibDACLowerLimit!, channel, val), scpiReturn(calibDACLowerLimit!)) +end +scpiCommand(::typeof(calibDACLowerLimit!), channel::Integer, val) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":LIM:LOW $(Float32(val))") +scpiReturn(::typeof(calibDACLowerLimit!)) = Bool + +""" +Retrieve the calibration DAC lower limit for given channel from the RedPitayas EEPROM. +""" +calibDACLowerLimit(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibDACLowerLimit, channel), scpiReturn(calibDACLowerLimit)) +scpiCommand(::typeof(calibDACLowerLimit), channel::Integer) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":LIM:LOW?") +scpiReturn(::typeof(calibDACLowerLimit)) = Float64 + +""" +Store calibration DAC upper limit `val` for given channel into the RedPitayas EEPROM. +This value is used by the server to limit the output voltage. +""" +function calibDACUpperLimit!(rp::RedPitaya, channel::Integer, val) + return query(rp, scpiCommand(calibDACUpperLimit!, channel, val), scpiReturn(calibDACUpperLimit!)) +end +scpiCommand(::typeof(calibDACUpperLimit!), channel::Integer, val) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":LIM:UP $(Float32(val))") +scpiReturn(::typeof(calibDACUpperLimit!)) = Bool + +""" +Retrieve the calibration DAC upper limit for given channel from the RedPitayas EEPROM. +""" +calibDACUpperLimit(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibDACUpperLimit, channel), scpiReturn(calibDACUpperLimit)) +scpiCommand(::typeof(calibDACUpperLimit), channel::Integer) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":LIM:UP?") +scpiReturn(::typeof(calibDACUpperLimit)) = Float64 + +""" +Applies `val` with a positive sign as the upper and with a negative sign as the lower calibration DAC limit. + +See also [calibDACUpperLimit!](@ref), [calibDACLowerLimit!](@ref) +""" +function calibDACLimit!(rp::RedPitaya, channel::Integer, val) + val = abs(val) + result = calibDACLowerLimit!(rp, channel, -val) + result &= calibDACUpperLimit!(rp, channel, val) + return result +end + """ Store calibration ADC offset `val` for given channel into the RedPitayas EEPROM. Absolute value has to be smaller than 1.0 V. -See also [convertSamplesToPeriods](@ref),[convertSamplesToFrames](@ref). +See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ function calibADCOffset!(rp::RedPitaya, channel::Integer, val) if abs(val) > 1.0 @@ -73,7 +112,7 @@ scpiReturn(::typeof(calibADCOffset!)) = Bool """ Retrieve the calibration ADC offset for given channel from the RedPitayas EEPROM. -See also [convertSamplesToPeriods](@ref),[convertSamplesToFrames](@ref). +See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ function calibADCOffset(rp::RedPitaya, channel::Integer) return query(rp, scpiCommand(calibADCOffset, channel), scpiReturn(calibADCOffset)) @@ -83,7 +122,7 @@ scpiReturn(::typeof(calibADCOffset)) = Float64 """ Store calibration ADC scale `val` for given channel into the RedPitayas EEPROM. -See also [convertSamplesToPeriods](@ref),[convertSamplesToFrames](@ref). +See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ function calibADCScale!(rp::RedPitaya, channel::Integer, val) rp.calib[1, channel] = Float32(val) @@ -97,7 +136,7 @@ scpiReturn(::typeof(calibADCScale!)) = Bool """ Retrieve the calibration ADC scale for given channel from the RedPitayas EEPROM. -See also [convertSamplesToPeriods](@ref),[convertSamplesToFrames](@ref). +See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ function calibADCScale(rp::RedPitaya, channel::Integer) return query(rp, scpiCommand(calibADCScale, channel), scpiReturn(calibADCScale)) @@ -109,6 +148,11 @@ calibFlags(rp::RedPitaya) = query(rp, scpiCommand(calibFlags), scpiReturn(calibF scpiCommand(::typeof(calibFlags)) = "RP:CALib:FLAGs" scpiReturn(::typeof(calibFlags)) = Int64 +""" +Update the cached calibration values. + +See also [calibADCScale](@ref), [calibADCOffset](@ref). +""" function updateCalib!(rp::RedPitaya) rp.calib[1, 1] = calibADCScale(rp, 1) rp.calib[2, 1] = calibADCOffset(rp, 1) diff --git a/src/client/julia/src/RedPitayaDAQServer.jl b/src/client/julia/src/RedPitayaDAQServer.jl index b23977dc..3e8ea76d 100644 --- a/src/client/julia/src/RedPitayaDAQServer.jl +++ b/src/client/julia/src/RedPitayaDAQServer.jl @@ -97,7 +97,12 @@ Receive a String from the RedPitaya command socket. Reads until a whole line is """ receive(rp::RedPitaya) = readline(rp.socket)[1:end] -receive(rp::RedPitaya, ch::Channel) = put!(ch, receive(rp)) +""" +Receive a String from the RedPitaya command socket. Reads until a whole line is received and puts it in the supplied channel `ch`. +""" +function receive(rp::RedPitaya, ch::Channel) + put!(ch, receive(rp)) +end """ Receive a string from the RedPitaya command socket. Reads until a whole line is received or timeout seconds passed. @@ -177,6 +182,12 @@ function connect(rp::RedPitaya) if length(temp) > 0 @warn "RP $(rp.host): Channels $(string(temp)) have a small DAC scale calibration value. If this is not intended use calibDACScale!(rp, i, 1.0) to set a default scale." end + + imageVersion = imgversion(rp) + packageVersion = pkgversion(@__MODULE__) + if packageVersion.minor != imageVersion + @warn "RedPitayaDAQServer $(rp.host) (minor) client version ($packageVersion) differs from FPGA image version ($imageVersion). Incompatible (minor) versions can result in undefined behaviour" + end end end end @@ -221,6 +232,10 @@ function stringToEnum(enumType::Type{T}, value::AbstractString) where {T <: Enum return instances(enumType)[index] end +imgversion(rp::RedPitaya) = query(rp, scpiCommand(imgversion), scpiReturn(imgversion)) +scpiCommand(::typeof(imgversion)) = "RP:VERsion:IMAGe?" +scpiReturn(::typeof(imgversion)) = UInt32 + """ Represent the different modes the server can be in. Valid values are `CONFIGURATION`, `ACQUISITION` and `TRANSMISSION`. @@ -303,6 +318,7 @@ push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K <: Function, T <: Tuple} = pus function push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K <: Function, T <: Any} return push!(batch, cmd.first => (cmd.second,)) end + """ Remove the last added command from the batch """ diff --git a/src/client/julia/src/Sequence.jl b/src/client/julia/src/Sequence.jl index 16b1bb4c..e89b08cf 100644 --- a/src/client/julia/src/Sequence.jl +++ b/src/client/julia/src/Sequence.jl @@ -19,6 +19,7 @@ Return the number of sequence channel. seqChan(rp::RedPitaya) = query(rp, scpiCommand(seqChan), scpiReturn(seqChan)) scpiCommand(::typeof(seqChan)) = "RP:DAC:SEQ:CHan?" scpiReturn(::typeof(seqChan)) = Int64 + """ Set the number of sequence channel. Valid values are between `1` and `6`. Return `true` if the command was successful. """ @@ -37,6 +38,7 @@ Return the number of samples per sequence step. samplesPerStep(rp::RedPitaya) = query(rp, scpiCommand(samplesPerStep), scpiReturn(samplesPerStep)) scpiCommand(::typeof(samplesPerStep)) = "RP:DAC:SEQ:SAMP?" scpiReturn(::typeof(samplesPerStep)) = Int64 + """ Set the number of samples per sequence step. Return `true` if the command was successful. """ @@ -88,8 +90,6 @@ length(seq::SequenceLUT) = seq.repetitions * size(seq.values, 2) """ Abstract struct of client-side representation of a sequence. - -See [`appendSequence!`](@ref), [`prepareSequence!`](@ref), [`ArbitrarySequence`](@ref). """ abstract type AbstractSequence end @@ -176,6 +176,7 @@ struct HoldBorderRampingSequence <: RampingSequence enable::Union{Array{Bool}, Nothing} rampUp::SequenceLUT rampDown::SequenceLUT + """ Constructor for `HoldBorderRampingSequence`. @@ -230,6 +231,7 @@ struct ConstantRampingSequence <: RampingSequence lut::SequenceLUT enable::Union{Array{Bool}, Nothing} ramping::SequenceLUT + function ConstantRampingSequence( lut::Array{Float32}, repetitions::Integer, @@ -349,8 +351,6 @@ end """ Transmit the client-side representation `seq` to the server and append it to the current list of sequences. Return `true` if the required commands were successful. - -See [`prepareSequence!`](@ref), [`clearSequences!`](@ref). """ function sequence!(rp::RedPitaya, seq::AbstractSequence) result = true diff --git a/src/client/julia/src/SlowIO.jl b/src/client/julia/src/SlowIO.jl index 46d39147..ec3a7c96 100644 --- a/src/client/julia/src/SlowIO.jl +++ b/src/client/julia/src/SlowIO.jl @@ -157,7 +157,12 @@ julia> DIO!(rp, DIO7_P, true) true ``` """ -DIO!(rp::RedPitaya, pin, val) = query(rp, scpiCommand(DIO!, pin, val), scpiReturn(DIO!)) +function DIO!(rp::RedPitaya, pin, val) + if DIODirection(rp, pin) == DIO_IN + @warn "The pin $pin is currently set as input. The value is applied anyways!" + end + return query(rp, scpiCommand(DIO!, pin, val), scpiReturn(DIO!)) +end function scpiCommand(::typeof(DIO!), pin::DIOPins, val::Bool) return string("RP:DIO:", DIOPinToCommand(pin), " ", (val ? "ON" : "OFF")) end @@ -174,7 +179,12 @@ julia > DIO(rp, DIO7_P) true ``` """ -DIO(rp::RedPitaya, pin) = parseReturn(DIO, query(rp, scpiCommand(DIO, pin), scpiReturn(DIO))) +function DIO(rp::RedPitaya, pin) + if DIODirection(rp, pin) == DIO_OUT + @warn "The pin $pin is currently set as output. The value is read anyways but might be wrong!" + end + return parseReturn(DIO, query(rp, scpiCommand(DIO, pin), scpiReturn(DIO))) +end scpiCommand(::typeof(DIO), pin) = string("RP:DIO:", DIOPinToCommand(pin), "?") scpiReturn(::typeof(DIO)) = String parseReturn(::typeof(DIO), ret) = occursin("ON", ret) diff --git a/src/fpga/bd/bd.tcl b/src/fpga/bd/bd.tcl index 5c24f765..765faf17 100644 --- a/src/fpga/bd/bd.tcl +++ b/src/fpga/bd/bd.tcl @@ -901,6 +901,8 @@ proc create_hier_cell_signal_compose1 { parentCell nameHier } { connect_bd_net -net offset_1 [get_bd_pins offset] [get_bd_pins signal_composer_0/seq] connect_bd_net -net rst_ps7_0_125M_peripheral_aresetn [get_bd_pins aresetn] [get_bd_pins signal_ramp_0/aresetn] [get_bd_pins waveform_awg1/aresetn] [get_bd_pins waveform_gen_0/aresetn] [get_bd_pins waveform_gen_1/aresetn] [get_bd_pins waveform_gen_2/aresetn] [get_bd_pins waveform_gen_3/aresetn] connect_bd_net -net signal_calib_0_signal_out [get_bd_pins S] [get_bd_pins signal_calib_0/signal_out] + connect_bd_net -net signal_cfg_slice_0_calib_limit_lower [get_bd_pins signal_calib_0/calib_limit_lower] [get_bd_pins signal_cfg_slice_0/calib_limit_lower] + connect_bd_net -net signal_cfg_slice_0_calib_limit_upper [get_bd_pins signal_calib_0/calib_limit_upper] [get_bd_pins signal_cfg_slice_0/calib_limit_upper] connect_bd_net -net signal_cfg_slice_0_calib_offset [get_bd_pins signal_calib_0/calib_offset] [get_bd_pins signal_cfg_slice_0/calib_offset] connect_bd_net -net signal_cfg_slice_0_calib_scale [get_bd_pins signal_calib_0/calib_scale] [get_bd_pins signal_cfg_slice_0/calib_scale] connect_bd_net -net signal_cfg_slice_0_comp_0_amp [get_bd_pins signal_cfg_slice_0/comp_0_amp] [get_bd_pins waveform_gen_0/amplitude] @@ -1093,6 +1095,8 @@ proc create_hier_cell_signal_compose { parentCell nameHier } { connect_bd_net -net offset_1 [get_bd_pins offset] [get_bd_pins signal_composer_0/seq] connect_bd_net -net ramping_enable_1 [get_bd_pins enable_ramping] [get_bd_pins signal_ramp/enableRamping] connect_bd_net -net signal_calib_0_signal_out [get_bd_pins S] [get_bd_pins signal_calib_0/signal_out] + connect_bd_net -net signal_cfg_slice_0_calib_limit_lower [get_bd_pins signal_calib_0/calib_limit_lower] [get_bd_pins signal_cfg_slice_0/calib_limit_lower] + connect_bd_net -net signal_cfg_slice_0_calib_limit_upper [get_bd_pins signal_calib_0/calib_limit_upper] [get_bd_pins signal_cfg_slice_0/calib_limit_upper] connect_bd_net -net signal_cfg_slice_0_calib_offset [get_bd_pins signal_calib_0/calib_offset] [get_bd_pins signal_cfg_slice_0/calib_offset] connect_bd_net -net signal_cfg_slice_0_calib_scale [get_bd_pins signal_calib_0/calib_scale] [get_bd_pins signal_cfg_slice_0/calib_scale] connect_bd_net -net signal_cfg_slice_0_comp_0_amp [get_bd_pins signal_cfg_slice_0/comp_0_amp] [get_bd_pins waveform_gen_0/amplitude] @@ -1514,7 +1518,7 @@ proc create_hier_cell_system_1 { parentCell nameHier } { # Create instance: axi_interconnect_0, and set properties set axi_interconnect_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_0 ] set_property -dict [ list \ - CONFIG.NUM_MI {9} \ + CONFIG.NUM_MI {10} \ ] $axi_interconnect_0 # Create instance: axi_interconnect_1, and set properties @@ -1548,6 +1552,13 @@ proc create_hier_cell_system_1 { parentCell nameHier } { CONFIG.STS_DATA_WIDTH {64} \ ] $axi_sts_register_counter_trigger + # Create instance: axi_sts_register_image_version, and set properties + set axi_sts_register_image_version [ create_bd_cell -type ip -vlnv pavel-demin:user:axi_sts_register:1.0 axi_sts_register_image_version ] + set_property -dict [ list \ + CONFIG.AXI_ADDR_WIDTH {32} \ + CONFIG.STS_DATA_WIDTH {32} \ + ] $axi_sts_register_image_version + # Create instance: axi_sts_register_pdm, and set properties set axi_sts_register_pdm [ create_bd_cell -type ip -vlnv pavel-demin:user:axi_sts_register:1.0 axi_sts_register_pdm ] set_property -dict [ list \ @@ -1562,6 +1573,13 @@ proc create_hier_cell_system_1 { parentCell nameHier } { CONFIG.STS_DATA_WIDTH {32} \ ] $axi_sts_register_reset + # Create instance: image_version, and set properties + set image_version [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 image_version ] + set_property -dict [ list \ + CONFIG.CONST_VAL {7} \ + CONFIG.CONST_WIDTH {32} \ + ] $image_version + # Create instance: processing_system7_0, and set properties set processing_system7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ] set_property -dict [ list \ @@ -1999,6 +2017,7 @@ unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#una connect_bd_intf_net -intf_net axi_interconnect_0_M06_AXI [get_bd_intf_pins axi_interconnect_0/M06_AXI] [get_bd_intf_pins axi_sts_register_reset/S_AXI] connect_bd_intf_net -intf_net axi_interconnect_0_M07_AXI [get_bd_intf_pins axi_interconnect_0/M07_AXI] [get_bd_intf_pins axi_sts_register_counter_trigger/S_AXI] connect_bd_intf_net -intf_net axi_interconnect_0_M08_AXI [get_bd_intf_pins axi_cfg_register_counter_trigger/S_AXI] [get_bd_intf_pins axi_interconnect_0/M08_AXI] + connect_bd_intf_net -intf_net axi_interconnect_0_M09_AXI [get_bd_intf_pins axi_interconnect_0/M09_AXI] [get_bd_intf_pins axi_sts_register_image_version/S_AXI] connect_bd_intf_net -intf_net axi_interconnect_1_M00_AXI [get_bd_intf_pins seq_bram] [get_bd_intf_pins axi_interconnect_1/M00_AXI] connect_bd_intf_net -intf_net axi_interconnect_1_M01_AXI [get_bd_intf_pins awg_0_bram] [get_bd_intf_pins axi_interconnect_1/M01_AXI] connect_bd_intf_net -intf_net axi_interconnect_1_M02_AXI [get_bd_intf_pins awg_1_bram] [get_bd_intf_pins axi_interconnect_1/M02_AXI] @@ -2012,12 +2031,13 @@ unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#unassigned#una connect_bd_net -net axi_cfg_register_1_cfg_data1 [get_bd_pins dac_cfg] [get_bd_pins axi_cfg_register_dac/cfg_data] connect_bd_net -net axi_cfg_register_1_cfg_data2 [get_bd_pins cfg_data] [get_bd_pins axi_cfg_register_cfg/cfg_data] connect_bd_net -net axi_cfg_register_axi_sts_register_counter_trigger_cfg_data [get_bd_pins counter_trigger_cfg] [get_bd_pins axi_cfg_register_counter_trigger/cfg_data] - connect_bd_net -net clk_wiz_0_clk_internal [get_bd_pins S_AXI_HP0_ACLK] [get_bd_pins axi_cfg_register_cfg/aclk] [get_bd_pins axi_cfg_register_counter_trigger/aclk] [get_bd_pins axi_cfg_register_dac/aclk] [get_bd_pins axi_interconnect_0/ACLK] [get_bd_pins axi_interconnect_0/M00_ACLK] [get_bd_pins axi_interconnect_0/M01_ACLK] [get_bd_pins axi_interconnect_0/M02_ACLK] [get_bd_pins axi_interconnect_0/M03_ACLK] [get_bd_pins axi_interconnect_0/M04_ACLK] [get_bd_pins axi_interconnect_0/M05_ACLK] [get_bd_pins axi_interconnect_0/M06_ACLK] [get_bd_pins axi_interconnect_0/M07_ACLK] [get_bd_pins axi_interconnect_0/M08_ACLK] [get_bd_pins axi_interconnect_0/S00_ACLK] [get_bd_pins axi_interconnect_1/ACLK] [get_bd_pins axi_interconnect_1/M00_ACLK] [get_bd_pins axi_interconnect_1/M01_ACLK] [get_bd_pins axi_interconnect_1/M02_ACLK] [get_bd_pins axi_interconnect_1/S00_ACLK] [get_bd_pins axi_sts_register_DIOIn/aclk] [get_bd_pins axi_sts_register_adc/aclk] [get_bd_pins axi_sts_register_counter_trigger/aclk] [get_bd_pins axi_sts_register_pdm/aclk] [get_bd_pins axi_sts_register_reset/aclk] [get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] [get_bd_pins processing_system7_0/M_AXI_GP1_ACLK] [get_bd_pins processing_system7_0/S_AXI_HP0_ACLK] [get_bd_pins rst_ps7_0_125M/slowest_sync_clk] + connect_bd_net -net clk_wiz_0_clk_internal [get_bd_pins S_AXI_HP0_ACLK] [get_bd_pins axi_cfg_register_cfg/aclk] [get_bd_pins axi_cfg_register_counter_trigger/aclk] [get_bd_pins axi_cfg_register_dac/aclk] [get_bd_pins axi_interconnect_0/ACLK] [get_bd_pins axi_interconnect_0/M00_ACLK] [get_bd_pins axi_interconnect_0/M01_ACLK] [get_bd_pins axi_interconnect_0/M02_ACLK] [get_bd_pins axi_interconnect_0/M03_ACLK] [get_bd_pins axi_interconnect_0/M04_ACLK] [get_bd_pins axi_interconnect_0/M05_ACLK] [get_bd_pins axi_interconnect_0/M06_ACLK] [get_bd_pins axi_interconnect_0/M07_ACLK] [get_bd_pins axi_interconnect_0/M08_ACLK] [get_bd_pins axi_interconnect_0/M09_ACLK] [get_bd_pins axi_interconnect_0/S00_ACLK] [get_bd_pins axi_interconnect_1/ACLK] [get_bd_pins axi_interconnect_1/M00_ACLK] [get_bd_pins axi_interconnect_1/M01_ACLK] [get_bd_pins axi_interconnect_1/M02_ACLK] [get_bd_pins axi_interconnect_1/S00_ACLK] [get_bd_pins axi_sts_register_DIOIn/aclk] [get_bd_pins axi_sts_register_adc/aclk] [get_bd_pins axi_sts_register_counter_trigger/aclk] [get_bd_pins axi_sts_register_image_version/aclk] [get_bd_pins axi_sts_register_pdm/aclk] [get_bd_pins axi_sts_register_reset/aclk] [get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] [get_bd_pins processing_system7_0/M_AXI_GP1_ACLK] [get_bd_pins processing_system7_0/S_AXI_HP0_ACLK] [get_bd_pins rst_ps7_0_125M/slowest_sync_clk] connect_bd_net -net clk_wiz_0_locked [get_bd_pins dcm_locked] [get_bd_pins rst_ps7_0_125M/dcm_locked] + connect_bd_net -net image_version_dout [get_bd_pins axi_sts_register_image_version/sts_data] [get_bd_pins image_version/dout] connect_bd_net -net processing_system7_0_FCLK_CLK0 [get_bd_pins FCLK_CLK0] [get_bd_pins processing_system7_0/FCLK_CLK0] connect_bd_net -net processing_system7_0_FCLK_RESET0_N [get_bd_pins FCLK_RESET0_N] [get_bd_pins processing_system7_0/FCLK_RESET0_N] connect_bd_net -net rst_ps7_0_125M_interconnect_aresetn [get_bd_pins axi_interconnect_0/ARESETN] [get_bd_pins axi_interconnect_1/ARESETN] [get_bd_pins rst_ps7_0_125M/interconnect_aresetn] - connect_bd_net -net rst_ps7_0_125M_peripheral_aresetn [get_bd_pins peripheral_aresetn] [get_bd_pins axi_cfg_register_cfg/aresetn] [get_bd_pins axi_cfg_register_counter_trigger/aresetn] [get_bd_pins axi_cfg_register_dac/aresetn] [get_bd_pins axi_interconnect_0/M00_ARESETN] [get_bd_pins axi_interconnect_0/M01_ARESETN] [get_bd_pins axi_interconnect_0/M02_ARESETN] [get_bd_pins axi_interconnect_0/M03_ARESETN] [get_bd_pins axi_interconnect_0/M04_ARESETN] [get_bd_pins axi_interconnect_0/M05_ARESETN] [get_bd_pins axi_interconnect_0/M06_ARESETN] [get_bd_pins axi_interconnect_0/M07_ARESETN] [get_bd_pins axi_interconnect_0/M08_ARESETN] [get_bd_pins axi_interconnect_0/S00_ARESETN] [get_bd_pins axi_interconnect_1/M00_ARESETN] [get_bd_pins axi_interconnect_1/M01_ARESETN] [get_bd_pins axi_interconnect_1/M02_ARESETN] [get_bd_pins axi_interconnect_1/S00_ARESETN] [get_bd_pins axi_sts_register_DIOIn/aresetn] [get_bd_pins axi_sts_register_adc/aresetn] [get_bd_pins axi_sts_register_counter_trigger/aresetn] [get_bd_pins axi_sts_register_pdm/aresetn] [get_bd_pins axi_sts_register_reset/aresetn] [get_bd_pins rst_ps7_0_125M/peripheral_aresetn] + connect_bd_net -net rst_ps7_0_125M_peripheral_aresetn [get_bd_pins peripheral_aresetn] [get_bd_pins axi_cfg_register_cfg/aresetn] [get_bd_pins axi_cfg_register_counter_trigger/aresetn] [get_bd_pins axi_cfg_register_dac/aresetn] [get_bd_pins axi_interconnect_0/M00_ARESETN] [get_bd_pins axi_interconnect_0/M01_ARESETN] [get_bd_pins axi_interconnect_0/M02_ARESETN] [get_bd_pins axi_interconnect_0/M03_ARESETN] [get_bd_pins axi_interconnect_0/M04_ARESETN] [get_bd_pins axi_interconnect_0/M05_ARESETN] [get_bd_pins axi_interconnect_0/M06_ARESETN] [get_bd_pins axi_interconnect_0/M07_ARESETN] [get_bd_pins axi_interconnect_0/M08_ARESETN] [get_bd_pins axi_interconnect_0/M09_ARESETN] [get_bd_pins axi_interconnect_0/S00_ARESETN] [get_bd_pins axi_interconnect_1/M00_ARESETN] [get_bd_pins axi_interconnect_1/M01_ARESETN] [get_bd_pins axi_interconnect_1/M02_ARESETN] [get_bd_pins axi_interconnect_1/S00_ARESETN] [get_bd_pins axi_sts_register_DIOIn/aresetn] [get_bd_pins axi_sts_register_adc/aresetn] [get_bd_pins axi_sts_register_counter_trigger/aresetn] [get_bd_pins axi_sts_register_image_version/aresetn] [get_bd_pins axi_sts_register_pdm/aresetn] [get_bd_pins axi_sts_register_reset/aresetn] [get_bd_pins rst_ps7_0_125M/peripheral_aresetn] connect_bd_net -net sts_data1_1 [get_bd_pins curr_pdm_values] [get_bd_pins axi_sts_register_pdm/sts_data] connect_bd_net -net sts_data_1 [get_bd_pins reset_sts] [get_bd_pins axi_sts_register_reset/sts_data] connect_bd_net -net sts_data_2 [get_bd_pins sts_data] [get_bd_pins axi_sts_register_DIOIn/sts_data] @@ -3404,6 +3424,7 @@ proc create_root_design { parentCell } { assign_bd_address -offset 0x40006000 -range 0x00001000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs system/axi_sts_register_DIOIn/s_axi/reg0] -force assign_bd_address -offset 0x40001000 -range 0x00001000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs system/axi_sts_register_adc/s_axi/reg0] -force assign_bd_address -offset 0x40007000 -range 0x00001000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs system/axi_sts_register_counter_trigger/s_axi/reg0] -force + assign_bd_address -offset 0x40009000 -range 0x00001000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs system/axi_sts_register_image_version/s_axi/reg0] -force assign_bd_address -offset 0x40003000 -range 0x00001000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs system/axi_sts_register_pdm/s_axi/reg0] -force assign_bd_address -offset 0x40005000 -range 0x00001000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs system/axi_sts_register_reset/s_axi/reg0] -force assign_bd_address -offset 0x40010000 -range 0x00010000 -target_address_space [get_bd_addr_spaces system/processing_system7_0/Data] [get_bd_addr_segs xadc_wiz_0/s_axi_lite/Reg] -force @@ -3413,6 +3434,7 @@ proc create_root_design { parentCell } { # Restore current instance current_bd_instance $oldCurInst + validate_bd_design save_bd_design } # End of create_root_design() @@ -3425,5 +3447,3 @@ proc create_root_design { parentCell } { create_root_design "" -common::send_gid_msg -ssname BD::TCL -id 2053 -severity "WARNING" "This Tcl script was generated from a block design that has not been validated. It is possible that design <$design_name> may result in errors during validation." - diff --git a/src/fpga/hdl/reset_manager.v b/src/fpga/hdl/reset_manager.v index b1769238..7e891778 100644 --- a/src/fpga/hdl/reset_manager.v +++ b/src/fpga/hdl/reset_manager.v @@ -212,7 +212,7 @@ begin end else begin - triggerState <= trigger_in_int || sata_trigger_int; + triggerState <= trigger_in_int; end masterTriggerState_pre <= reset_cfg[5] & counter_trigger; // counter_trigger must always be high if not enabled diff --git a/src/fpga/hdl/signal_cfg_slice.v b/src/fpga/hdl/signal_cfg_slice.v index 9e38a60c..47df5ec9 100644 --- a/src/fpga/hdl/signal_cfg_slice.v +++ b/src/fpga/hdl/signal_cfg_slice.v @@ -6,6 +6,8 @@ module signal_cfg_slice( output [15:0] offset, output [15:0] calib_scale, output [15:0] calib_offset, + output [15:0] calib_limit_lower, + output [15:0] calib_limit_upper, output [47:0] comp_0_cfg, output [15:0] comp_0_amp, output [47:0] comp_0_freq, @@ -35,13 +37,15 @@ assign calib_scale[15:0] = cfg_data[191:176]; assign comp_0_phase[47:0] = cfg_data[239:192]; assign calib_offset[15:0] = cfg_data[255:240]; +// 0 bit gap assign comp_1_cfg[47:0] = cfg_data[303:256]; assign comp_1_amp[15:0] = cfg_data[319:304]; assign comp_1_freq[47:0] = cfg_data[367:320]; -// 15 bit gap +assign calib_limit_lower[15:0] = cfg_data[383:368]; assign comp_1_phase[47:0] = cfg_data[431:384]; +assign calib_limit_upper[15:0] = cfg_data[447:432]; -// 15 bit gap +// 0 bit gap assign comp_2_cfg[47:0] = cfg_data[495:448]; assign comp_2_amp[15:0] = cfg_data[511:496]; assign comp_2_freq[47:0] = cfg_data[559:512]; diff --git a/src/fpga/hdl/signal_limit.v b/src/fpga/hdl/signal_limit.v new file mode 100644 index 00000000..fb005086 --- /dev/null +++ b/src/fpga/hdl/signal_limit.v @@ -0,0 +1,26 @@ +`timescale 1ns / 1ps + + +module signal_limit( + input clk, + input signed [15:0] signal_in, + input signed [15:0] limit_upper, + input signed[15:0] limit_lower, + output [15:0] limited_signal + ); + +reg signed [15:0] signal_result; + +always @(posedge clk) +begin + if (signal_in > limit_upper) begin + signal_result <= limit_upper; + end else if (signal_in < limit_lower) begin + signal_result <= limit_lower; + end else begin + signal_result <= signal_in; + end +end + +assign limited_signal = signal_result; +endmodule diff --git a/src/lib/rp-daq-lib.c b/src/lib/rp-daq-lib.c index 51844e31..93a4b10e 100644 --- a/src/lib/rp-daq-lib.c +++ b/src/lib/rp-daq-lib.c @@ -26,6 +26,7 @@ uint32_t *counter_trigger_sts; uint16_t *pdm_cfg; uint64_t *adc_sts, *dac_cfg; uint32_t *awg_0_cfg, *awg_1_cfg; +uint32_t *version_sts; volatile int32_t *xadc; // static const uint32_t ANALOG_OUT_MASK = 0xFF; @@ -91,6 +92,11 @@ bool isZynq7045() { return (getFPGAId() == 0x11); } + +uint32_t getFPGAImageVersion() { + return *version_sts; +} + void loadBitstream() { if(!access("/tmp/bitstreamLoaded", F_OK )){ printf("Bitfile already loaded\n"); @@ -150,9 +156,13 @@ int init() { xadc = mmap(NULL, 16*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x40010000); awg_0_cfg = mmap(NULL, AWG_BUFF_SIZE*sizeof(uint32_t)/2, PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x80020000); awg_1_cfg = mmap(NULL, AWG_BUFF_SIZE*sizeof(uint32_t)/2, PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x80028000); + version_sts = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x40009000); + + loadBitstream(); - + printf("FPGA Image Version %u\n", getFPGAImageVersion()); + calib_Init(); // Load calibration from EEPROM calib_validate(&calib); printf("Using calibration version %d with: %u\n", calib.version, calib.set_flags); @@ -463,6 +473,39 @@ int setCalibDACOffset(float value, int channel) { return 0; } +int setCalibDACLowerLimit(float value, int channel) { + if (channel < 0 || channel > 1) { + return -3; + } + + int16_t limit = (int16_t)(value*8191.0); + if (limit < -8191 || limit >= 8192) { + return -2; + } + // Config lower limit is stored in second component freq + uint64_t register_value = *(dac_cfg + COMPONENT_START_OFFSET + FREQ_OFFSET + COMPONENT_OFFSET*1 + CHANNEL_OFFSET*channel); + register_value = (register_value & MASK_LOWER_48) | ((((int64_t) limit) << 48) & ~MASK_LOWER_48); + *(dac_cfg + COMPONENT_START_OFFSET + FREQ_OFFSET + COMPONENT_OFFSET*1 + CHANNEL_OFFSET*channel) = register_value; + return 0; +} + + +int setCalibDACUpperLimit(float value, int channel) { + if (channel < 0 || channel > 1) { + return -3; + } + + int16_t limit = (int16_t)(value*8191.0); + if (limit < -8191 || limit >= 8192) { + return -2; + } + // Config upper limit is stored in second component phase + uint64_t register_value = *(dac_cfg + COMPONENT_START_OFFSET + PHASE_OFFSET + COMPONENT_OFFSET*1 + CHANNEL_OFFSET*channel); + register_value = (register_value & MASK_LOWER_48) | ((((int64_t) limit) << 48) & ~MASK_LOWER_48); + *(dac_cfg + COMPONENT_START_OFFSET + PHASE_OFFSET + COMPONENT_OFFSET*1 + CHANNEL_OFFSET*channel) = register_value; + return 0; +} + int setArbitraryWaveform(float* values, int channel) { uint32_t *awg_cfg = NULL; if (channel == 0) { @@ -882,10 +925,10 @@ int getMasterTrigger() { int setMasterTrigger(int mode) { if(mode == OFF) { setKeepAliveReset(ON); - double waitTime = getSamplesPerStep() * getDecimation() / 125e6; - usleep( 10*waitTime * 1000000); + //double waitTime = getSamplesPerStep() * getDecimation() / 125e6; + //usleep( 10*waitTime * 1000000); *((uint8_t *)(cfg + 1)) &= ~(1 << 5); - usleep( 10*waitTime * 1000000); + //usleep( 10*waitTime * 1000000); setRAMWriterMode(ADC_MODE_TRIGGERED); setKeepAliveReset(OFF); } else if(mode == ON) { @@ -1392,6 +1435,10 @@ rp_calib_params_t getDefaultCalib(){ calib.adc_ch1_offs = 0.0; calib.adc_ch2_fs = 1.0/8192.0; calib.adc_ch2_offs = 0.0; + calib.dac_ch1_lower = -1.0; + calib.dac_ch1_upper = 1.0; + calib.dac_ch2_lower = -1.0; + calib.dac_ch2_upper = 1.0; return calib; } @@ -1440,14 +1487,37 @@ int calib_validate(rp_calib_params_t * calib_params) { calib_params->dac_ch2_offs = def.dac_ch2_offs; calib_params->set_flags &= ~(1 << 7); } + + // DAC Limits + if (useDefault || !(calib_params->set_flags & (1 << 8))) { + calib_params->dac_ch1_lower = def.dac_ch1_lower; + calib_params->set_flags &= ~(1 << 8); + } + if (useDefault || !(calib_params->set_flags & (1 << 9))) { + calib_params->dac_ch1_upper = def.dac_ch1_upper; + calib_params->set_flags &= ~(1 << 9); + } + if (useDefault || !(calib_params->set_flags & (1 << 10))) { + calib_params->dac_ch2_lower = def.dac_ch2_lower; + calib_params->set_flags &= ~(1 << 10); + } + if (useDefault || !(calib_params->set_flags & (1 << 11))) { + calib_params->dac_ch2_upper = def.dac_ch2_upper; + calib_params->set_flags &= ~(1 << 11); + } + return 0; } int calib_apply() { setCalibDACScale(calib.dac_ch1_fs, 0); setCalibDACOffset(calib.dac_ch1_offs, 0); + setCalibDACLowerLimit(calib.dac_ch1_lower, 0); + setCalibDACUpperLimit(calib.dac_ch1_upper, 0); setCalibDACScale(calib.dac_ch2_fs, 1); setCalibDACOffset(calib.dac_ch2_offs, 1); + setCalibDACLowerLimit(calib.dac_ch2_lower, 1); + setCalibDACUpperLimit(calib.dac_ch2_upper, 1); return 0; } @@ -1511,6 +1581,36 @@ int calib_setDACScale(rp_calib_params_t * calib_params, float value, int channel return 0; } +int calib_setDACLowerLimit(rp_calib_params_t * calib_params, float value, int channel) { + if (channel < 0 || channel > 1) { + return -3; + } + if (channel == 0) { + calib_params->dac_ch1_lower = value; + calib_params->set_flags |= (1 << 8); + } + else if (channel == 1) { + calib_params->dac_ch2_lower = value; + calib_params->set_flags |= (1 << 10); + } + return 0; +} + +int calib_setDACUpperLimit(rp_calib_params_t * calib_params, float value, int channel) { + if (channel < 0 || channel > 1) { + return -3; + } + if (channel == 0) { + calib_params->dac_ch1_upper = value; + calib_params->set_flags |= (1 << 9); + } + else if (channel == 1) { + calib_params->dac_ch2_upper = value; + calib_params->set_flags |= (1 << 11); + } + return 0; +} + void calib_SetToZero() { calib = getDefaultCalib(); diff --git a/src/lib/rp-daq-lib.h b/src/lib/rp-daq-lib.h index 5b4644b1..649a6dcc 100644 --- a/src/lib/rp-daq-lib.h +++ b/src/lib/rp-daq-lib.h @@ -56,7 +56,7 @@ #define DIO_IN 1 #define DIO_OUT 0 -#define CALIB_VERSION 1 +#define CALIB_VERSION 2 extern bool verbose; @@ -68,6 +68,7 @@ extern uint32_t *counter_trigger_cfg, *counter_trigger_sts; extern uint16_t *pdm_cfg; extern uint64_t *adc_sts, *dac_cfg; extern uint32_t *awg_0_cfg, *awg_1_cfg; +extern uint32_t *version_sts; // init routines extern uint32_t getFPGAId(); @@ -78,6 +79,7 @@ extern bool isZynq7030(); extern bool isZynq7045(); extern int init(); extern void loadBitstream(); +extern uint32_t getFPGAImageVersion(); // fast DAC extern uint16_t getAmplitude(int, int); @@ -96,6 +98,8 @@ extern int getSignalType(int, int); extern int setSignalType(int, int, int); extern int setCalibDACScale(float, int); extern int setCalibDACOffset(float, int); +extern int setCalibDACLowerLimit(float, int); +extern int setCalibDACUpperLimit(float, int); extern int setArbitraryWaveform(float*, int); //extern int getRampingPeriod(int); @@ -200,7 +204,7 @@ extern void stopTx(); typedef struct { char id[3+1]; int version; - uint8_t set_flags; + uint16_t set_flags; float adc_ch1_fs; float adc_ch1_offs; float adc_ch2_fs; @@ -209,6 +213,10 @@ typedef struct { float dac_ch1_offs; float dac_ch2_fs; float dac_ch2_offs; + float dac_ch1_lower; + float dac_ch1_upper; + float dac_ch2_lower; + float dac_ch2_upper; } rp_calib_params_t; extern int calib_Init(); @@ -220,6 +228,8 @@ extern int calib_setADCOffset(rp_calib_params_t * calib_params, float value, int extern int calib_setADCScale(rp_calib_params_t * calib_params, float value, int channel); extern int calib_setDACOffset(rp_calib_params_t * calib_params, float value, int channel); extern int calib_setDACScale(rp_calib_params_t * calib_params, float value, int channel); +extern int calib_setDACLowerLimit(rp_calib_params_t * calib_params, float value, int channel); +extern int calib_setDACUpperLimit(rp_calib_params_t * calib_params, float value, int channel); extern rp_calib_params_t calib_GetParams(); extern rp_calib_params_t calib_GetDefaultCalib(); diff --git a/src/server/scpi_commands.c b/src/server/scpi_commands.c index d0402f4f..7629ba5b 100644 --- a/src/server/scpi_commands.c +++ b/src/server/scpi_commands.c @@ -75,6 +75,11 @@ scpi_choice_def_t server_modes[] = { SCPI_CHOICE_LIST_END }; +static scpi_result_t RP_GetImageVersion(scpi_t * context) { + SCPI_ResultUInt32(context, getFPGAImageVersion()); + return SCPI_RES_OK; +} + static scpi_result_t RP_GetServerMode(scpi_t * context) { const char * name; SCPI_ChoiceToName(server_modes, getServerMode(), &name); @@ -1485,6 +1490,90 @@ static scpi_result_t RP_Calib_DAC_SetScale(scpi_t* context) { return returnSCPIBool(context, true); } +static scpi_result_t RP_Calib_DAC_GetLowerLimit(scpi_t* context) { + int32_t numbers[1]; + SCPI_CommandNumbers(context, numbers, 1, 1); + int channel = numbers[0]; + + rp_calib_params_t calib_params = calib_GetParams(); + if (channel == 0) { + SCPI_ResultFloat(context, calib_params.dac_ch1_lower); + } + else if (channel == 1) { + SCPI_ResultFloat(context, calib_params.dac_ch2_lower); + } + else { + SCPI_ResultFloat(context, NAN); + return SCPI_RES_ERR; + } + return SCPI_RES_OK; +} + +static scpi_result_t RP_Calib_DAC_SetLowerLimit(scpi_t* context) { + if (getServerMode() != CONFIGURATION) { + return returnSCPIBool(context, false); + } + + int32_t numbers[1]; + SCPI_CommandNumbers(context, numbers, 1, 1); + int channel = numbers[0]; + + rp_calib_params_t calib_params = calib_GetParams(); + float limit; + + SCPI_ParamFloat(context, &limit, true); + if (calib_setDACLowerLimit(&calib_params, limit, channel)) { + return returnSCPIBool(context, false); + } + + calib_WriteParams(calib_params, false); + calib_Init(); // Reload from cache from EEPROM + calib_apply(); + return returnSCPIBool(context, true); +} + +static scpi_result_t RP_Calib_DAC_GetUpperLimit(scpi_t* context) { + int32_t numbers[1]; + SCPI_CommandNumbers(context, numbers, 1, 1); + int channel = numbers[0]; + + rp_calib_params_t calib_params = calib_GetParams(); + if (channel == 0) { + SCPI_ResultFloat(context, calib_params.dac_ch1_upper); + } + else if (channel == 1) { + SCPI_ResultFloat(context, calib_params.dac_ch2_upper); + } + else { + SCPI_ResultFloat(context, NAN); + return SCPI_RES_ERR; + } + return SCPI_RES_OK; +} + +static scpi_result_t RP_Calib_DAC_SetUpperLimit(scpi_t* context) { + if (getServerMode() != CONFIGURATION) { + return returnSCPIBool(context, false); + } + + int32_t numbers[1]; + SCPI_CommandNumbers(context, numbers, 1, 1); + int channel = numbers[0]; + + rp_calib_params_t calib_params = calib_GetParams(); + float limit; + + SCPI_ParamFloat(context, &limit, true); + if (calib_setDACUpperLimit(&calib_params, limit, channel)) { + return returnSCPIBool(context, false); + } + + calib_WriteParams(calib_params, false); + calib_Init(); // Reload from cache from EEPROM + calib_apply(); + return returnSCPIBool(context, true); +} + static scpi_result_t RP_Calib_ADC_GetOffset(scpi_t* context) { int32_t numbers[1]; SCPI_CommandNumbers(context, numbers, 1, 1); @@ -1616,6 +1705,7 @@ const scpi_command_t scpi_commands[] = { {.pattern = "STATus:PRESet", .callback = SCPI_StatusPreset,}, /* RP-DAQ */ + {.pattern = "RP:VERsion:IMAGe?", .callback = RP_GetImageVersion,}, {.pattern = "RP:MODe?", .callback = RP_GetServerMode,}, {.pattern = "RP:MODe", .callback = RP_SetServerMode,}, // DAC @@ -1724,6 +1814,10 @@ const scpi_command_t scpi_commands[] = { {.pattern = "RP:CALib:DAC:CHannel#:OFFset", .callback = RP_Calib_DAC_SetOffset,}, {.pattern = "RP:CALib:DAC:CHannel#:SCAle?", .callback = RP_Calib_DAC_GetScale,}, {.pattern = "RP:CALib:DAC:CHannel#:SCAle", .callback = RP_Calib_DAC_SetScale,}, + {.pattern = "RP:CALib:DAC:CHannel#:LIMit:LOWer?", .callback = RP_Calib_DAC_GetLowerLimit,}, + {.pattern = "RP:CALib:DAC:CHannel#:LIMit:LOWer", .callback = RP_Calib_DAC_SetLowerLimit,}, + {.pattern = "RP:CALib:DAC:CHannel#:LIMit:UPper?", .callback = RP_Calib_DAC_GetUpperLimit,}, + {.pattern = "RP:CALib:DAC:CHannel#:LIMit:UPper", .callback = RP_Calib_DAC_SetUpperLimit,}, {.pattern = "RP:CALib:ADC:CHannel#:OFFset?", .callback = RP_Calib_ADC_GetOffset,}, {.pattern = "RP:CALib:ADC:CHannel#:OFFset", .callback = RP_Calib_ADC_SetOffset,}, {.pattern = "RP:CALib:ADC:CHannel#:SCAle?", .callback = RP_Calib_ADC_GetScale,},